1/*  Part of SWI-Prolog
2
3    Author:        Jan Wielemaker
4    E-mail:        J.Wielemaker@vu.nl
5    WWW:           http://www.swi-prolog.org
6    Copyright (c)  1985-2020, University of Amsterdam
7                              VU University Amsterdam
8                              CWI, Amsterdam
9    All rights reserved.
10
11    Redistribution and use in source and binary forms, with or without
12    modification, are permitted provided that the following conditions
13    are met:
14
15    1. Redistributions of source code must retain the above copyright
16       notice, this list of conditions and the following disclaimer.
17
18    2. Redistributions in binary form must reproduce the above copyright
19       notice, this list of conditions and the following disclaimer in
20       the documentation and/or other materials provided with the
21       distribution.
22
23    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26    FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
27    COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
29    BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
31    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
33    ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34    POSSIBILITY OF SUCH DAMAGE.
35*/
36
37/*
38Consult, derivates and basic things.   This  module  is  loaded  by  the
39C-written  bootstrap  compiler.
40
41The $:- directive  is  executed  by  the  bootstrap  compiler,  but  not
42inserted  in  the  intermediate  code  file.   Used  to print diagnostic
43messages and start the Prolog defined compiler for  the  remaining  boot
44modules.
45
46If you want  to  debug  this  module,  put  a  '$:-'(trace).   directive
47somewhere.   The  tracer will work properly under boot compilation as it
48will use the C defined write predicate  to  print  goals  and  does  not
49attempt to call the Prolog defined trace interceptor.
50*/
51
52                /********************************
53                *    LOAD INTO MODULE SYSTEM    *
54                ********************************/
55
56:- '$set_source_module'(system).
57
58'$boot_message'(_Format, _Args) :-
59    current_prolog_flag(verbose, silent),
60    !.
61'$boot_message'(Format, Args) :-
62    format(Format, Args),
63    !.
64
65'$:-'('$boot_message'('Loading boot file ...~n', [])).
66
67
68                /********************************
69                *          DIRECTIVES           *
70                *********************************/
71
72:- meta_predicate
73    dynamic(:),
74    multifile(:),
75    public(:),
76    module_transparent(:),
77    discontiguous(:),
78    volatile(:),
79    thread_local(:),
80    noprofile(:),
81    non_terminal(:),
82    '$clausable'(:),
83    '$iso'(:),
84    '$hide'(:).
85
86%!  dynamic(+Spec) is det.
87%!  multifile(+Spec) is det.
88%!  module_transparent(+Spec) is det.
89%!  discontiguous(+Spec) is det.
90%!  volatile(+Spec) is det.
91%!  thread_local(+Spec) is det.
92%!  noprofile(+Spec) is det.
93%!  public(+Spec) is det.
94%!  non_terminal(+Spec) is det.
95%
96%   Predicate versions of standard  directives   that  set predicate
97%   attributes. These predicates bail out with an error on the first
98%   failure (typically permission errors).
99
100%!  '$iso'(+Spec) is det.
101%
102%   Set the ISO  flag.  This  defines   that  the  predicate  cannot  be
103%   redefined inside a module.
104
105%!  '$clausable'(+Spec) is det.
106%
107%   Specify that we can run  clause/2  on   a  predicate,  even if it is
108%   static. ISO specifies that `public` also   plays  this role. in SWI,
109%   `public` means that the predicate can be   called, even if we cannot
110%   find a reference to it.
111
112%!  '$hide'(+Spec) is det.
113%
114%   Specify that the predicate cannot be seen in the debugger.
115
116dynamic(Spec)            :- '$set_pattr'(Spec, pred, dynamic(true)).
117multifile(Spec)          :- '$set_pattr'(Spec, pred, multifile(true)).
118module_transparent(Spec) :- '$set_pattr'(Spec, pred, transparent(true)).
119discontiguous(Spec)      :- '$set_pattr'(Spec, pred, discontiguous(true)).
120volatile(Spec)           :- '$set_pattr'(Spec, pred, volatile(true)).
121thread_local(Spec)       :- '$set_pattr'(Spec, pred, thread_local(true)).
122noprofile(Spec)          :- '$set_pattr'(Spec, pred, noprofile(true)).
123public(Spec)             :- '$set_pattr'(Spec, pred, public(true)).
124non_terminal(Spec)       :- '$set_pattr'(Spec, pred, non_terminal(true)).
125'$iso'(Spec)             :- '$set_pattr'(Spec, pred, iso(true)).
126'$clausable'(Spec)       :- '$set_pattr'(Spec, pred, clausable(true)).
127'$hide'(Spec)            :- '$set_pattr'(Spec, pred, trace(false)).
128
129'$set_pattr'(M:Pred, How, Attr) :-
130    '$set_pattr'(Pred, M, How, Attr).
131
132%!  '$set_pattr'(+Spec, +Module, +From, +Attr)
133%
134%   Set predicate attributes. From is one of `pred` or `directive`.
135
136'$set_pattr'(X, _, _, _) :-
137    var(X),
138    '$uninstantiation_error'(X).
139'$set_pattr'(as(Spec,Options), M, How, Attr0) :-
140    !,
141    '$attr_options'(Options, Attr0, Attr),
142    '$set_pattr'(Spec, M, How, Attr).
143'$set_pattr'([], _, _, _) :- !.
144'$set_pattr'([H|T], M, How, Attr) :-           % ISO
145    !,
146    '$set_pattr'(H, M, How, Attr),
147    '$set_pattr'(T, M, How, Attr).
148'$set_pattr'((A,B), M, How, Attr) :-           % ISO and traditional
149    !,
150    '$set_pattr'(A, M, How, Attr),
151    '$set_pattr'(B, M, How, Attr).
152'$set_pattr'(M:T, _, How, Attr) :-
153    !,
154    '$set_pattr'(T, M, How, Attr).
155'$set_pattr'(PI, M, _, []) :-
156    !,
157    '$pi_head'(M:PI, Pred),
158    (   '$get_predicate_attribute'(Pred, incremental, 1)
159    ->  '$wrap_incremental'(Pred)
160    ;   '$unwrap_incremental'(Pred)
161    ).
162'$set_pattr'(A, M, How, [O|OT]) :-
163    !,
164    '$set_pattr'(A, M, How, O),
165    '$set_pattr'(A, M, How, OT).
166'$set_pattr'(A, M, pred, Attr) :-
167    !,
168    Attr =.. [Name,Val],
169    '$set_pi_attr'(M:A, Name, Val).
170'$set_pattr'(A, M, directive, Attr) :-
171    !,
172    Attr =.. [Name,Val],
173    catch('$set_pi_attr'(M:A, Name, Val),
174          error(E, _),
175          print_message(error, error(E, context((Name)/1,_)))).
176
177'$set_pi_attr'(PI, Name, Val) :-
178    '$pi_head'(PI, Head),
179    '$set_predicate_attribute'(Head, Name, Val).
180
181'$attr_options'(Var, _, _) :-
182    var(Var),
183    !,
184    '$uninstantiation_error'(Var).
185'$attr_options'((A,B), Attr0, Attr) :-
186    !,
187    '$attr_options'(A, Attr0, Attr1),
188    '$attr_options'(B, Attr1, Attr).
189'$attr_options'(Opt, Attr0, Attrs) :-
190    '$must_be'(ground, Opt),
191    (   '$attr_option'(Opt, AttrX)
192    ->  (   is_list(Attr0)
193        ->  '$join_attrs'(AttrX, Attr0, Attrs)
194        ;   '$join_attrs'(AttrX, [Attr0], Attrs)
195        )
196    ;   '$domain_error'(predicate_option, Opt)
197    ).
198
199'$join_attrs'(Attr, Attrs, Attrs) :-
200    memberchk(Attr, Attrs),
201    !.
202'$join_attrs'(Attr, Attrs, Attrs) :-
203    Attr =.. [Name,Value],
204    Gen =.. [Name,Existing],
205    memberchk(Gen, Attrs),
206    !,
207    throw(error(conflict_error(Name, Value, Existing), _)).
208'$join_attrs'(Attr, Attrs0, Attrs) :-
209    '$append'(Attrs0, [Attr], Attrs).
210
211'$attr_option'(incremental, incremental(true)).
212'$attr_option'(opaque, incremental(false)).
213'$attr_option'(abstract(Level0), abstract(Level)) :-
214    '$table_option'(Level0, Level).
215'$attr_option'(subgoal_abstract(Level0), subgoal_abstract(Level)) :-
216    '$table_option'(Level0, Level).
217'$attr_option'(answer_abstract(Level0), answer_abstract(Level)) :-
218    '$table_option'(Level0, Level).
219'$attr_option'(max_answers(Level0), max_answers(Level)) :-
220    '$table_option'(Level0, Level).
221'$attr_option'(volatile, volatile(true)).
222'$attr_option'(multifile, multifile(true)).
223'$attr_option'(discontiguous, discontiguous(true)).
224'$attr_option'(shared, thread_local(false)).
225'$attr_option'(local, thread_local(true)).
226'$attr_option'(private, thread_local(true)).
227
228'$table_option'(Value0, _Value) :-
229    var(Value0),
230    !,
231    '$instantiation_error'(Value0).
232'$table_option'(Value0, Value) :-
233    integer(Value0),
234    Value0 >= 0,
235    !,
236    Value = Value0.
237'$table_option'(off, -1) :-
238    !.
239'$table_option'(false, -1) :-
240    !.
241'$table_option'(infinite, -1) :-
242    !.
243'$table_option'(Value, _) :-
244    '$domain_error'(nonneg_or_false, Value).
245
246
247%!  '$pattr_directive'(+Spec, +Module) is det.
248%
249%   This implements the directive version of dynamic/1, multifile/1,
250%   etc. This version catches and prints   errors.  If the directive
251%   specifies  multiple  predicates,  processing    after  an  error
252%   continues with the remaining predicates.
253
254'$pattr_directive'(dynamic(Spec), M) :-
255    '$set_pattr'(Spec, M, directive, dynamic(true)).
256'$pattr_directive'(multifile(Spec), M) :-
257    '$set_pattr'(Spec, M, directive, multifile(true)).
258'$pattr_directive'(module_transparent(Spec), M) :-
259    '$set_pattr'(Spec, M, directive, transparent(true)).
260'$pattr_directive'(discontiguous(Spec), M) :-
261    '$set_pattr'(Spec, M, directive, discontiguous(true)).
262'$pattr_directive'(volatile(Spec), M) :-
263    '$set_pattr'(Spec, M, directive, volatile(true)).
264'$pattr_directive'(thread_local(Spec), M) :-
265    '$set_pattr'(Spec, M, directive, thread_local(true)).
266'$pattr_directive'(noprofile(Spec), M) :-
267    '$set_pattr'(Spec, M, directive, noprofile(true)).
268'$pattr_directive'(public(Spec), M) :-
269    '$set_pattr'(Spec, M, directive, public(true)).
270
271%!  '$pi_head'(?PI, ?Head)
272
273'$pi_head'(PI, Head) :-
274    var(PI),
275    var(Head),
276    '$instantiation_error'([PI,Head]).
277'$pi_head'(M:PI, M:Head) :-
278    !,
279    '$pi_head'(PI, Head).
280'$pi_head'(Name/Arity, Head) :-
281    !,
282    '$head_name_arity'(Head, Name, Arity).
283'$pi_head'(Name//DCGArity, Head) :-
284    !,
285    (   nonvar(DCGArity)
286    ->  Arity is DCGArity+2,
287        '$head_name_arity'(Head, Name, Arity)
288    ;   '$head_name_arity'(Head, Name, Arity),
289        DCGArity is Arity - 2
290    ).
291'$pi_head'(PI, _) :-
292    '$type_error'(predicate_indicator, PI).
293
294%!  '$head_name_arity'(+Goal, -Name, -Arity).
295%!  '$head_name_arity'(-Goal, +Name, +Arity).
296
297'$head_name_arity'(Goal, Name, Arity) :-
298    (   atom(Goal)
299    ->  Name = Goal, Arity = 0
300    ;   compound(Goal)
301    ->  compound_name_arity(Goal, Name, Arity)
302    ;   var(Goal)
303    ->  (   Arity == 0
304        ->  (   atom(Name)
305            ->  Goal = Name
306            ;   blob(Name, closure)
307            ->  Goal = Name
308            ;   '$type_error'(atom, Name)
309            )
310        ;   compound_name_arity(Goal, Name, Arity)
311        )
312    ;   '$type_error'(callable, Goal)
313    ).
314
315:- '$iso'(((dynamic)/1, (multifile)/1, (discontiguous)/1)).
316
317
318                /********************************
319                *       CALLING, CONTROL        *
320                *********************************/
321
322:- noprofile((call/1,
323              catch/3,
324              once/1,
325              ignore/1,
326              call_cleanup/2,
327              call_cleanup/3,
328              setup_call_cleanup/3,
329              setup_call_catcher_cleanup/4)).
330
331:- meta_predicate
332    ';'(0,0),
333    ','(0,0),
334    @(0,+),
335    call(0),
336    call(1,?),
337    call(2,?,?),
338    call(3,?,?,?),
339    call(4,?,?,?,?),
340    call(5,?,?,?,?,?),
341    call(6,?,?,?,?,?,?),
342    call(7,?,?,?,?,?,?,?),
343    not(0),
344    \+(0),
345    '->'(0,0),
346    '*->'(0,0),
347    once(0),
348    ignore(0),
349    catch(0,?,0),
350    reset(0,?,-),
351    setup_call_cleanup(0,0,0),
352    setup_call_catcher_cleanup(0,0,?,0),
353    call_cleanup(0,0),
354    call_cleanup(0,?,0),
355    catch_with_backtrace(0,?,0),
356    '$meta_call'(0).
357
358:- '$iso'((call/1, (\+)/1, once/1, (;)/2, (',')/2, (->)/2, catch/3)).
359
360% The control structures are always compiled, both   if they appear in a
361% clause body and if they are handed  to   call/1.  The only way to call
362% these predicates is by means of  call/2..   In  that case, we call the
363% hole control structure again to get it compiled by call/1 and properly
364% deal  with  !,  etc.  Another  reason  for  having  these  things   as
365% predicates is to be able to define   properties for them, helping code
366% analyzers.
367
368(M0:If ; M0:Then) :- !, call(M0:(If ; Then)).
369(M1:If ; M2:Then) :-    call(M1:(If ; M2:Then)).
370(G1   , G2)       :-    call((G1   , G2)).
371(If  -> Then)     :-    call((If  -> Then)).
372(If *-> Then)     :-    call((If *-> Then)).
373@(Goal,Module)    :-    @(Goal,Module).
374
375%!  '$meta_call'(:Goal)
376%
377%   Interpreted  meta-call  implementation.  By    default,   call/1
378%   compiles its argument into  a   temporary  clause. This realises
379%   better  performance  if  the  (complex)  goal   does  a  lot  of
380%   backtracking  because  this   interpreted    version   needs  to
381%   re-interpret the remainder of the goal after backtracking.
382%
383%   This implementation is used by  reset/3 because the continuation
384%   cannot be captured if it contains   a  such a compiled temporary
385%   clause.
386
387'$meta_call'(M:G) :-
388    prolog_current_choice(Ch),
389    '$meta_call'(G, M, Ch).
390
391'$meta_call'(Var, _, _) :-
392    var(Var),
393    !,
394    '$instantiation_error'(Var).
395'$meta_call'((A,B), M, Ch) :-
396    !,
397    '$meta_call'(A, M, Ch),
398    '$meta_call'(B, M, Ch).
399'$meta_call'((I->T;E), M, Ch) :-
400    !,
401    (   prolog_current_choice(Ch2),
402        '$meta_call'(I, M, Ch2)
403    ->  '$meta_call'(T, M, Ch)
404    ;   '$meta_call'(E, M, Ch)
405    ).
406'$meta_call'((I*->T;E), M, Ch) :-
407    !,
408    (   prolog_current_choice(Ch2),
409        '$meta_call'(I, M, Ch2)
410    *-> '$meta_call'(T, M, Ch)
411    ;   '$meta_call'(E, M, Ch)
412    ).
413'$meta_call'((I->T), M, Ch) :-
414    !,
415    (   prolog_current_choice(Ch2),
416        '$meta_call'(I, M, Ch2)
417    ->  '$meta_call'(T, M, Ch)
418    ).
419'$meta_call'((I*->T), M, Ch) :-
420    !,
421    prolog_current_choice(Ch2),
422    '$meta_call'(I, M, Ch2),
423    '$meta_call'(T, M, Ch).
424'$meta_call'((A;B), M, Ch) :-
425    !,
426    (   '$meta_call'(A, M, Ch)
427    ;   '$meta_call'(B, M, Ch)
428    ).
429'$meta_call'(\+(G), M, _) :-
430    !,
431    prolog_current_choice(Ch),
432    \+ '$meta_call'(G, M, Ch).
433'$meta_call'(call(G), M, _) :-
434    !,
435    prolog_current_choice(Ch),
436    '$meta_call'(G, M, Ch).
437'$meta_call'(M:G, _, Ch) :-
438    !,
439    '$meta_call'(G, M, Ch).
440'$meta_call'(!, _, Ch) :-
441    prolog_cut_to(Ch).
442'$meta_call'(G, M, _Ch) :-
443    call(M:G).
444
445%!  call(:Closure, ?A).
446%!  call(:Closure, ?A1, ?A2).
447%!  call(:Closure, ?A1, ?A2, ?A3).
448%!  call(:Closure, ?A1, ?A2, ?A3, ?A4).
449%!  call(:Closure, ?A1, ?A2, ?A3, ?A4, ?A5).
450%!  call(:Closure, ?A1, ?A2, ?A3, ?A4, ?A5, ?A6).
451%!  call(:Closure, ?A1, ?A2, ?A3, ?A4, ?A5, ?A6, ?A7).
452%
453%   Arity 2..8 is demanded by the   ISO standard. Higher arities are
454%   supported, but handled by the compiler.   This  implies they are
455%   not backed up by predicates and   analyzers  thus cannot ask for
456%   their  properties.  Analyzers  should    hard-code  handling  of
457%   call/2..
458
459:- '$iso'((call/2,
460           call/3,
461           call/4,
462           call/5,
463           call/6,
464           call/7,
465           call/8)).
466
467call(Goal) :-                           % make these available as predicates
468    Goal.
469call(Goal, A) :-
470    call(Goal, A).
471call(Goal, A, B) :-
472    call(Goal, A, B).
473call(Goal, A, B, C) :-
474    call(Goal, A, B, C).
475call(Goal, A, B, C, D) :-
476    call(Goal, A, B, C, D).
477call(Goal, A, B, C, D, E) :-
478    call(Goal, A, B, C, D, E).
479call(Goal, A, B, C, D, E, F) :-
480    call(Goal, A, B, C, D, E, F).
481call(Goal, A, B, C, D, E, F, G) :-
482    call(Goal, A, B, C, D, E, F, G).
483
484%!  not(:Goal) is semidet.
485%
486%   Pre-ISO version of \+/1. Note that  some systems define not/1 as
487%   a logically more sound version of \+/1.
488
489not(Goal) :-
490    \+ Goal.
491
492%!  \+(:Goal) is semidet.
493%
494%   Predicate version that allows for meta-calling.
495
496\+ Goal :-
497    \+ Goal.
498
499%!  once(:Goal) is semidet.
500%
501%   ISO predicate, acting as call((Goal, !)).
502
503once(Goal) :-
504    Goal,
505    !.
506
507%!  ignore(:Goal) is det.
508%
509%   Call Goal, cut choice-points on success  and succeed on failure.
510%   intended for calling side-effects and proceed on failure.
511
512ignore(Goal) :-
513    Goal,
514    !.
515ignore(_Goal).
516
517:- '$iso'((false/0)).
518
519%!  false.
520%
521%   Synonym for fail/0, providing a declarative reading.
522
523false :-
524    fail.
525
526%!  catch(:Goal, +Catcher, :Recover)
527%
528%   ISO compliant exception handling.
529
530catch(_Goal, _Catcher, _Recover) :-
531    '$catch'.                       % Maps to I_CATCH, I_EXITCATCH
532
533%!  prolog_cut_to(+Choice)
534%
535%   Cut all choice points after Choice
536
537prolog_cut_to(_Choice) :-
538    '$cut'.                         % Maps to I_CUTCHP
539
540%!  reset(:Goal, ?Ball, -Continue)
541%
542%   Delimited continuation support.
543
544reset(_Goal, _Ball, _Cont) :-
545    '$reset'.
546
547%!  shift(+Ball)
548%
549%   Shift control back to the enclosing reset/3
550
551shift(Ball) :-
552    '$shift'(Ball).
553
554%!  call_continuation(+Continuation:list)
555%
556%   Call a continuation as created  by   shift/1.  The continuation is a
557%   list of '$cont$'(Clause, PC, EnvironmentArg,   ...)  structures. The
558%   predicate  '$call_one_tail_body'/1  creates   a    frame   from  the
559%   continuation and calls this.
560%
561%   Note that we can technically also  push the entire continuation onto
562%   the environment and  call  it.  Doing   it  incrementally  as  below
563%   exploits last-call optimization  and   therefore  possible quadratic
564%   expansion of the continuation.
565
566call_continuation([]).
567call_continuation([TB|Rest]) :-
568    (   Rest == []
569    ->  '$call_continuation'(TB)
570    ;   '$call_continuation'(TB),
571        call_continuation(Rest)
572    ).
573
574%!  catch_with_backtrace(:Goal, ?Ball, :Recover)
575%
576%   As catch/3, but tell library(prolog_stack) to  record a backtrace in
577%   case of an exception.
578
579catch_with_backtrace(Goal, Ball, Recover) :-
580    catch(Goal, Ball, Recover),
581    '$no_lco'.
582
583'$no_lco'.
584
585%!  '$recover_and_rethrow'(:Goal, +Term)
586%
587%   This goal is used to wrap  the   catch/3  recover handler if the
588%   exception is not supposed to be   `catchable'.  An example of an
589%   uncachable exception is '$aborted', used   by abort/0. Note that
590%   we cut to ensure  that  the   exception  is  not delayed forever
591%   because the recover handler leaves a choicepoint.
592
593:- public '$recover_and_rethrow'/2.
594
595'$recover_and_rethrow'(Goal, Exception) :-
596    call_cleanup(Goal, throw(Exception)),
597    !.
598
599
600%!  setup_call_cleanup(:Setup, :Goal, :Cleanup).
601%!  setup_call_catcher_cleanup(:Setup, :Goal, +Catcher, :Cleanup).
602%!  call_cleanup(:Goal, :Cleanup).
603%!  call_cleanup(:Goal, +Catcher, :Cleanup).
604%
605%   Call Cleanup once after Goal is finished (deterministic success,
606%   failure, exception or  cut).  The   call  to  '$call_cleanup' is
607%   translated to I_CALLCLEANUP. This  instruction   relies  on  the
608%   exact stack layout left   by  setup_call_catcher_cleanup/4. Also
609%   the predicate name is used by   the kernel cleanup mechanism and
610%   can only be changed together with the kernel.
611
612setup_call_catcher_cleanup(Setup, _Goal, _Catcher, _Cleanup) :-
613    '$sig_atomic'(Setup),
614    '$call_cleanup'.
615
616setup_call_cleanup(Setup, Goal, Cleanup) :-
617    setup_call_catcher_cleanup(Setup, Goal, _Catcher, Cleanup).
618
619call_cleanup(Goal, Cleanup) :-
620    setup_call_catcher_cleanup(true, Goal, _Catcher, Cleanup).
621
622call_cleanup(Goal, Catcher, Cleanup) :-
623    setup_call_catcher_cleanup(true, Goal, Catcher, Cleanup).
624
625                 /*******************************
626                 *       INITIALIZATION         *
627                 *******************************/
628
629:- meta_predicate
630    initialization(0, +).
631
632:- multifile '$init_goal'/3.
633:- dynamic   '$init_goal'/3.
634
635%!  initialization(:Goal, +When)
636%
637%   Register Goal to be executed if a saved state is restored. In
638%   addition, the goal is executed depending on When:
639%
640%       * now
641%       Execute immediately
642%       * after_load
643%       Execute after loading the file in which it appears.  This
644%       is initialization/1.
645%       * restore_state
646%       Do not execute immediately, but only when restoring the
647%       state.  Not allowed in a sandboxed environment.
648%       * prepare_state
649%       Called before saving a state.  Can be used to clean the
650%       environment (see also volatile/1) or eagerly execute
651%       goals that are normally executed lazily.
652%       * program
653%       Works as =|-g goal|= goals.
654%       * main
655%       Starts the application.  Only last declaration is used.
656%
657%   Note that all goals are executed when a program is restored.
658
659initialization(Goal, When) :-
660    '$must_be'(oneof(atom, initialization_type,
661                     [ now,
662                       after_load,
663                       restore,
664                       restore_state,
665                       prepare_state,
666                       program,
667                       main
668                     ]), When),
669    '$initialization_context'(Source, Ctx),
670    '$initialization'(When, Goal, Source, Ctx).
671
672'$initialization'(now, Goal, _Source, Ctx) :-
673    '$run_init_goal'(Goal, Ctx),
674    '$compile_init_goal'(-, Goal, Ctx).
675'$initialization'(after_load, Goal, Source, Ctx) :-
676    (   Source \== (-)
677    ->  '$compile_init_goal'(Source, Goal, Ctx)
678    ;   throw(error(context_error(nodirective,
679                                  initialization(Goal, after_load)),
680                    _))
681    ).
682'$initialization'(restore, Goal, Source, Ctx) :- % deprecated
683    '$initialization'(restore_state, Goal, Source, Ctx).
684'$initialization'(restore_state, Goal, _Source, Ctx) :-
685    (   \+ current_prolog_flag(sandboxed_load, true)
686    ->  '$compile_init_goal'(-, Goal, Ctx)
687    ;   '$permission_error'(register, initialization(restore), Goal)
688    ).
689'$initialization'(prepare_state, Goal, _Source, Ctx) :-
690    (   \+ current_prolog_flag(sandboxed_load, true)
691    ->  '$compile_init_goal'(when(prepare_state), Goal, Ctx)
692    ;   '$permission_error'(register, initialization(restore), Goal)
693    ).
694'$initialization'(program, Goal, _Source, Ctx) :-
695    (   \+ current_prolog_flag(sandboxed_load, true)
696    ->  '$compile_init_goal'(when(program), Goal, Ctx)
697    ;   '$permission_error'(register, initialization(restore), Goal)
698    ).
699'$initialization'(main, Goal, _Source, Ctx) :-
700    (   \+ current_prolog_flag(sandboxed_load, true)
701    ->  '$compile_init_goal'(when(main), Goal, Ctx)
702    ;   '$permission_error'(register, initialization(restore), Goal)
703    ).
704
705
706'$compile_init_goal'(Source, Goal, Ctx) :-
707    atom(Source),
708    Source \== (-),
709    !,
710    '$store_admin_clause'(system:'$init_goal'(Source, Goal, Ctx),
711                          _Layout, Source, Ctx).
712'$compile_init_goal'(Source, Goal, Ctx) :-
713    assertz('$init_goal'(Source, Goal, Ctx)).
714
715
716%!  '$run_initialization'(?File, +Options) is det.
717%!  '$run_initialization'(?File, +Action, +Options) is det.
718%
719%   Run initialization directives for all files  if File is unbound,
720%   or for a specified file.   Note  that '$run_initialization'/2 is
721%   called from runInitialization() in pl-wic.c  for .qlf files. The
722%   '$run_initialization'/3 is called with Action   set  to `loaded`
723%   when called for a QLF file.
724
725'$run_initialization'(_, loaded, _) :- !.
726'$run_initialization'(File, _Action, Options) :-
727    '$run_initialization'(File, Options).
728
729'$run_initialization'(File, Options) :-
730    setup_call_cleanup(
731        '$start_run_initialization'(Options, Restore),
732        '$run_initialization_2'(File),
733        '$end_run_initialization'(Restore)).
734
735'$start_run_initialization'(Options, OldSandBoxed) :-
736    '$push_input_context'(initialization),
737    '$set_sandboxed_load'(Options, OldSandBoxed).
738'$end_run_initialization'(OldSandBoxed) :-
739    set_prolog_flag(sandboxed_load, OldSandBoxed),
740    '$pop_input_context'.
741
742'$run_initialization_2'(File) :-
743    (   '$init_goal'(File, Goal, Ctx),
744        File \= when(_),
745        '$run_init_goal'(Goal, Ctx),
746        fail
747    ;   true
748    ).
749
750'$run_init_goal'(Goal, Ctx) :-
751    (   catch_with_backtrace('$run_init_goal'(Goal), E,
752                             '$initialization_error'(E, Goal, Ctx))
753    ->  true
754    ;   '$initialization_failure'(Goal, Ctx)
755    ).
756
757:- multifile prolog:sandbox_allowed_goal/1.
758
759'$run_init_goal'(Goal) :-
760    current_prolog_flag(sandboxed_load, false),
761    !,
762    call(Goal).
763'$run_init_goal'(Goal) :-
764    prolog:sandbox_allowed_goal(Goal),
765    call(Goal).
766
767'$initialization_context'(Source, Ctx) :-
768    (   source_location(File, Line)
769    ->  Ctx = File:Line,
770        '$input_context'(Context),
771        '$top_file'(Context, File, Source)
772    ;   Ctx = (-),
773        File = (-)
774    ).
775
776'$top_file'([input(include, F1, _, _)|T], _, F) :-
777    !,
778    '$top_file'(T, F1, F).
779'$top_file'(_, F, F).
780
781
782'$initialization_error'(E, Goal, Ctx) :-
783    print_message(error, initialization_error(Goal, E, Ctx)).
784
785'$initialization_failure'(Goal, Ctx) :-
786    print_message(warning, initialization_failure(Goal, Ctx)).
787
788%!  '$clear_source_admin'(+File) is det.
789%
790%   Removes source adminstration related to File
791%
792%   @see Called from destroySourceFile() in pl-proc.c
793
794:- public '$clear_source_admin'/1.
795
796'$clear_source_admin'(File) :-
797    retractall('$init_goal'(_, _, File:_)),
798    retractall('$load_context_module'(File, _, _)),
799    retractall('$resolved_source_path'(_, File)).
800
801
802                 /*******************************
803                 *            STREAM            *
804                 *******************************/
805
806:- '$iso'(stream_property/2).
807stream_property(Stream, Property) :-
808    nonvar(Stream),
809    nonvar(Property),
810    !,
811    '$stream_property'(Stream, Property).
812stream_property(Stream, Property) :-
813    nonvar(Stream),
814    !,
815    '$stream_properties'(Stream, Properties),
816    '$member'(Property, Properties).
817stream_property(Stream, Property) :-
818    nonvar(Property),
819    !,
820    (   Property = alias(Alias),
821        atom(Alias)
822    ->  '$alias_stream'(Alias, Stream)
823    ;   '$streams_properties'(Property, Pairs),
824        '$member'(Stream-Property, Pairs)
825    ).
826stream_property(Stream, Property) :-
827    '$streams_properties'(Property, Pairs),
828    '$member'(Stream-Properties, Pairs),
829    '$member'(Property, Properties).
830
831
832                /********************************
833                *            MODULES            *
834                *********************************/
835
836%       '$prefix_module'(+Module, +Context, +Term, -Prefixed)
837%       Tags `Term' with `Module:' if `Module' is not the context module.
838
839'$prefix_module'(Module, Module, Head, Head) :- !.
840'$prefix_module'(Module, _, Head, Module:Head).
841
842%!  default_module(+Me, -Super) is multi.
843%
844%   Is true if `Super' is `Me' or a super (auto import) module of `Me'.
845
846default_module(Me, Super) :-
847    (   atom(Me)
848    ->  (   var(Super)
849        ->  '$default_module'(Me, Super)
850        ;   '$default_module'(Me, Super), !
851        )
852    ;   '$type_error'(module, Me)
853    ).
854
855'$default_module'(Me, Me).
856'$default_module'(Me, Super) :-
857    import_module(Me, S),
858    '$default_module'(S, Super).
859
860
861                /********************************
862                *      TRACE AND EXCEPTIONS     *
863                *********************************/
864
865:- dynamic   user:exception/3.
866:- multifile user:exception/3.
867
868%!  '$undefined_procedure'(+Module, +Name, +Arity, -Action) is det.
869%
870%   This predicate is called from C   on undefined predicates. First
871%   allows the user to take care of   it using exception/3. Else try
872%   to give a DWIM warning. Otherwise fail.   C  will print an error
873%   message.
874
875:- public
876    '$undefined_procedure'/4.
877
878'$undefined_procedure'(Module, Name, Arity, Action) :-
879    '$prefix_module'(Module, user, Name/Arity, Pred),
880    user:exception(undefined_predicate, Pred, Action0),
881    !,
882    Action = Action0.
883'$undefined_procedure'(Module, Name, Arity, Action) :-
884    \+ current_prolog_flag(autoload, false),
885    '$autoload'(Module:Name/Arity),
886    !,
887    Action = retry.
888'$undefined_procedure'(_, _, _, error).
889
890
891%!  '$loading'(+Library)
892%
893%   True if the library  is  being   loaded.  Just  testing that the
894%   predicate is defined is not  good  enough   as  the  file may be
895%   partly  loaded.  Calling  use_module/2  at   any  time  has  two
896%   drawbacks: it queries the filesystem,   causing  slowdown and it
897%   stops libraries being autoloaded from a   saved  state where the
898%   library is already loaded, but the source may not be accessible.
899
900'$loading'(Library) :-
901    current_prolog_flag(threads, true),
902    '$loading_file'(FullFile, _Queue, _LoadThread),
903    file_name_extension(Library, _, FullFile),
904    !.
905
906%        handle debugger 'w', 'p' and <N> depth options.
907
908'$set_debugger_write_options'(write) :-
909    !,
910    create_prolog_flag(debugger_write_options,
911                       [ quoted(true),
912                         attributes(dots),
913                         spacing(next_argument)
914                       ], []).
915'$set_debugger_write_options'(print) :-
916    !,
917    create_prolog_flag(debugger_write_options,
918                       [ quoted(true),
919                         portray(true),
920                         max_depth(10),
921                         attributes(portray),
922                         spacing(next_argument)
923                       ], []).
924'$set_debugger_write_options'(Depth) :-
925    current_prolog_flag(debugger_write_options, Options0),
926    (   '$select'(max_depth(_), Options0, Options)
927    ->  true
928    ;   Options = Options0
929    ),
930    create_prolog_flag(debugger_write_options,
931                       [max_depth(Depth)|Options], []).
932
933
934                /********************************
935                *        SYSTEM MESSAGES        *
936                *********************************/
937
938%!  '$confirm'(Spec)
939%
940%   Ask the user to confirm a question.  Spec is a term as used for
941%   print_message/2.
942
943'$confirm'(Spec) :-
944    print_message(query, Spec),
945    between(0, 5, _),
946        get_single_char(Answer),
947        (   '$in_reply'(Answer, 'yYjJ \n')
948        ->  !,
949            print_message(query, if_tty([yes-[]]))
950        ;   '$in_reply'(Answer, 'nN')
951        ->  !,
952            print_message(query, if_tty([no-[]])),
953            fail
954        ;   print_message(help, query(confirm)),
955            fail
956        ).
957
958'$in_reply'(Code, Atom) :-
959    char_code(Char, Code),
960    sub_atom(Atom, _, _, _, Char),
961    !.
962
963:- dynamic
964    user:portray/1.
965:- multifile
966    user:portray/1.
967
968
969                 /*******************************
970                 *       FILE_SEARCH_PATH       *
971                 *******************************/
972
973:- dynamic
974    user:file_search_path/2,
975    user:library_directory/1.
976:- multifile
977    user:file_search_path/2,
978    user:library_directory/1.
979
980user:(file_search_path(library, Dir) :-
981        library_directory(Dir)).
982user:file_search_path(swi, Home) :-
983    current_prolog_flag(home, Home).
984user:file_search_path(swi, Home) :-
985    current_prolog_flag(shared_home, Home).
986user:file_search_path(library, app_config(lib)).
987user:file_search_path(library, swi(library)).
988user:file_search_path(library, swi(library/clp)).
989user:file_search_path(foreign, swi(ArchLib)) :-
990    \+ current_prolog_flag(windows, true),
991    current_prolog_flag(arch, Arch),
992    atom_concat('lib/', Arch, ArchLib).
993user:file_search_path(foreign, swi(SoLib)) :-
994    (   current_prolog_flag(windows, true)
995    ->  SoLib = bin
996    ;   SoLib = lib
997    ).
998user:file_search_path(path, Dir) :-
999    getenv('PATH', Path),
1000    (   current_prolog_flag(windows, true)
1001    ->  atomic_list_concat(Dirs, (;), Path)
1002    ;   atomic_list_concat(Dirs, :, Path)
1003    ),
1004    '$member'(Dir, Dirs).
1005user:file_search_path(user_app_data, Dir) :-
1006    '$xdg_prolog_directory'(data, Dir).
1007user:file_search_path(common_app_data, Dir) :-
1008    '$xdg_prolog_directory'(common_data, Dir).
1009user:file_search_path(user_app_config, Dir) :-
1010    '$xdg_prolog_directory'(config, Dir).
1011user:file_search_path(common_app_config, Dir) :-
1012    '$xdg_prolog_directory'(common_config, Dir).
1013user:file_search_path(app_data, user_app_data('.')).
1014user:file_search_path(app_data, common_app_data('.')).
1015user:file_search_path(app_config, user_app_config('.')).
1016user:file_search_path(app_config, common_app_config('.')).
1017% backward compatibility
1018user:file_search_path(app_preferences, user_app_config('.')).
1019user:file_search_path(user_profile, app_preferences('.')).
1020
1021'$xdg_prolog_directory'(Which, Dir) :-
1022    '$xdg_directory'(Which, XDGDir),
1023    '$make_config_dir'(XDGDir),
1024    '$ensure_slash'(XDGDir, XDGDirS),
1025    atom_concat(XDGDirS, 'swi-prolog', Dir),
1026    '$make_config_dir'(Dir).
1027
1028% config
1029'$xdg_directory'(config, Home) :-
1030    current_prolog_flag(windows, true),
1031    catch(win_folder(appdata, Home), _, fail),
1032    !.
1033'$xdg_directory'(config, Home) :-
1034    getenv('XDG_CONFIG_HOME', Home).
1035'$xdg_directory'(config, Home) :-
1036    expand_file_name('~/.config', [Home]).
1037% data
1038'$xdg_directory'(data, Home) :-
1039    current_prolog_flag(windows, true),
1040    catch(win_folder(local_appdata, Home), _, fail),
1041    !.
1042'$xdg_directory'(data, Home) :-
1043    getenv('XDG_DATA_HOME', Home).
1044'$xdg_directory'(data, Home) :-
1045    expand_file_name('~/.local', [Local]),
1046    '$make_config_dir'(Local),
1047    atom_concat(Local, '/share', Home),
1048    '$make_config_dir'(Home).
1049% common data
1050'$xdg_directory'(common_data, Dir) :-
1051    current_prolog_flag(windows, true),
1052    catch(win_folder(common_appdata, Dir), _, fail),
1053    !.
1054'$xdg_directory'(common_data, Dir) :-
1055    '$existing_dir_from_env_path'('XDG_DATA_DIRS',
1056                                  [ '/usr/local/share',
1057                                    '/usr/share'
1058                                  ],
1059                                  Dir).
1060% common config
1061'$xdg_directory'(common_config, Dir) :-
1062    current_prolog_flag(windows, true),
1063    catch(win_folder(common_appdata, Dir), _, fail),
1064    !.
1065'$xdg_directory'(common_config, Dir) :-
1066    '$existing_dir_from_env_path'('XDG_CONFIG_DIRS', ['/etc/xdg'], Dir).
1067
1068'$existing_dir_from_env_path'(Env, Defaults, Dir) :-
1069    (   getenv(Env, Path)
1070    ->  '$path_sep'(Sep),
1071        atomic_list_concat(Dirs, Sep, Path)
1072    ;   Dirs = Defaults
1073    ),
1074    '$member'(Dir, Dirs),
1075    exists_directory(Dir).
1076
1077'$path_sep'(Char) :-
1078    (   current_prolog_flag(windows, true)
1079    ->  Char = ';'
1080    ;   Char = ':'
1081    ).
1082
1083'$make_config_dir'(Dir) :-
1084    exists_directory(Dir),
1085    !.
1086'$make_config_dir'(Dir) :-
1087    file_directory_name(Dir, Parent),
1088    '$my_file'(Parent),
1089    catch(make_directory(Dir), _, fail).
1090
1091'$ensure_slash'(Dir, DirS) :-
1092    (   sub_atom(Dir, _, _, 0, /)
1093    ->  DirS = Dir
1094    ;   atom_concat(Dir, /, DirS)
1095    ).
1096
1097
1098%!  expand_file_search_path(+Spec, -Expanded) is nondet.
1099%
1100%   Expand a search path.  The system uses depth-first search upto a
1101%   specified depth.  If this depth is exceeded an exception is raised.
1102%   TBD: bread-first search?
1103
1104expand_file_search_path(Spec, Expanded) :-
1105    catch('$expand_file_search_path'(Spec, Expanded, 0, []),
1106          loop(Used),
1107          throw(error(loop_error(Spec), file_search(Used)))).
1108
1109'$expand_file_search_path'(Spec, Expanded, N, Used) :-
1110    functor(Spec, Alias, 1),
1111    !,
1112    user:file_search_path(Alias, Exp0),
1113    NN is N + 1,
1114    (   NN > 16
1115    ->  throw(loop(Used))
1116    ;   true
1117    ),
1118    '$expand_file_search_path'(Exp0, Exp1, NN, [Alias=Exp0|Used]),
1119    arg(1, Spec, Segments),
1120    '$segments_to_atom'(Segments, File),
1121    '$make_path'(Exp1, File, Expanded).
1122'$expand_file_search_path'(Spec, Path, _, _) :-
1123    '$segments_to_atom'(Spec, Path).
1124
1125'$make_path'(Dir, '.', Path) :-
1126    !,
1127    Path = Dir.
1128'$make_path'(Dir, File, Path) :-
1129    sub_atom(Dir, _, _, 0, /),
1130    !,
1131    atom_concat(Dir, File, Path).
1132'$make_path'(Dir, File, Path) :-
1133    atomic_list_concat([Dir, /, File], Path).
1134
1135
1136                /********************************
1137                *         FILE CHECKING         *
1138                *********************************/
1139
1140%!  absolute_file_name(+Term, -AbsoluteFile, +Options) is nondet.
1141%
1142%   Translate path-specifier into a full   path-name. This predicate
1143%   originates from Quintus was introduced  in SWI-Prolog very early
1144%   and  has  re-appeared  in  SICStus  3.9.0,  where  they  changed
1145%   argument order and added some options.   We addopted the SICStus
1146%   argument order, but still accept the original argument order for
1147%   compatibility reasons.
1148
1149absolute_file_name(Spec, Options, Path) :-
1150    '$is_options'(Options),
1151    \+ '$is_options'(Path),
1152    !,
1153    absolute_file_name(Spec, Path, Options).
1154absolute_file_name(Spec, Path, Options) :-
1155    '$must_be'(options, Options),
1156                    % get the valid extensions
1157    (   '$select_option'(extensions(Exts), Options, Options1)
1158    ->  '$must_be'(list, Exts)
1159    ;   '$option'(file_type(Type), Options)
1160    ->  '$must_be'(atom, Type),
1161        '$file_type_extensions'(Type, Exts),
1162        Options1 = Options
1163    ;   Options1 = Options,
1164        Exts = ['']
1165    ),
1166    '$canonicalise_extensions'(Exts, Extensions),
1167                    % unless specified otherwise, ask regular file
1168    (   nonvar(Type)
1169    ->  Options2 = Options1
1170    ;   '$merge_options'(_{file_type:regular}, Options1, Options2)
1171    ),
1172                    % Det or nondet?
1173    (   '$select_option'(solutions(Sols), Options2, Options3)
1174    ->  '$must_be'(oneof(atom, solutions, [first,all]), Sols)
1175    ;   Sols = first,
1176        Options3 = Options2
1177    ),
1178                    % Errors or not?
1179    (   '$select_option'(file_errors(FileErrors), Options3, Options4)
1180    ->  '$must_be'(oneof(atom, file_errors, [error,fail]), FileErrors)
1181    ;   FileErrors = error,
1182        Options4 = Options3
1183    ),
1184                    % Expand shell patterns?
1185    (   atomic(Spec),
1186        '$select_option'(expand(Expand), Options4, Options5),
1187        '$must_be'(boolean, Expand)
1188    ->  expand_file_name(Spec, List),
1189        '$member'(Spec1, List)
1190    ;   Spec1 = Spec,
1191        Options5 = Options4
1192    ),
1193                    % Search for files
1194    (   Sols == first
1195    ->  (   '$chk_file'(Spec1, Extensions, Options5, true, Path)
1196        ->  !       % also kill choice point of expand_file_name/2
1197        ;   (   FileErrors == fail
1198            ->  fail
1199            ;   '$current_module'('$bags', _File),
1200                findall(P,
1201                        '$chk_file'(Spec1, Extensions, [access(exist)],
1202                                    false, P),
1203                        Candidates),
1204                '$abs_file_error'(Spec, Candidates, Options5)
1205            )
1206        )
1207    ;   '$chk_file'(Spec1, Extensions, Options5, false, Path)
1208    ).
1209
1210'$abs_file_error'(Spec, Candidates, Conditions) :-
1211    '$member'(F, Candidates),
1212    '$member'(C, Conditions),
1213    '$file_condition'(C),
1214    '$file_error'(C, Spec, F, E, Comment),
1215    !,
1216    throw(error(E, context(_, Comment))).
1217'$abs_file_error'(Spec, _, _) :-
1218    '$existence_error'(source_sink, Spec).
1219
1220'$file_error'(file_type(directory), Spec, File, Error, Comment) :-
1221    \+ exists_directory(File),
1222    !,
1223    Error = existence_error(directory, Spec),
1224    Comment = not_a_directory(File).
1225'$file_error'(file_type(_), Spec, File, Error, Comment) :-
1226    exists_directory(File),
1227    !,
1228    Error = existence_error(file, Spec),
1229    Comment = directory(File).
1230'$file_error'(access(OneOrList), Spec, File, Error, _) :-
1231    '$one_or_member'(Access, OneOrList),
1232    \+ access_file(File, Access),
1233    Error = permission_error(Access, source_sink, Spec).
1234
1235'$one_or_member'(Elem, List) :-
1236    is_list(List),
1237    !,
1238    '$member'(Elem, List).
1239'$one_or_member'(Elem, Elem).
1240
1241
1242'$file_type_extensions'(source, Exts) :-       % SICStus 3.9 compatibility
1243    !,
1244    '$file_type_extensions'(prolog, Exts).
1245'$file_type_extensions'(Type, Exts) :-
1246    '$current_module'('$bags', _File),
1247    !,
1248    findall(Ext, user:prolog_file_type(Ext, Type), Exts0),
1249    (   Exts0 == [],
1250        \+ '$ft_no_ext'(Type)
1251    ->  '$domain_error'(file_type, Type)
1252    ;   true
1253    ),
1254    '$append'(Exts0, [''], Exts).
1255'$file_type_extensions'(prolog, [pl, '']). % findall is not yet defined ...
1256
1257'$ft_no_ext'(txt).
1258'$ft_no_ext'(executable).
1259'$ft_no_ext'(directory).
1260
1261%!  user:prolog_file_type(?Extension, ?Type)
1262%
1263%   Define type of file based on the extension.  This is used by
1264%   absolute_file_name/3 and may be used to extend the list of
1265%   extensions used for some type.
1266%
1267%   Note that =qlf= must be last   when  searching for Prolog files.
1268%   Otherwise use_module/1 will consider  the   file  as  not-loaded
1269%   because the .qlf file is not  the   loaded  file.  Must be fixed
1270%   elsewhere.
1271
1272:- multifile(user:prolog_file_type/2).
1273:- dynamic(user:prolog_file_type/2).
1274
1275user:prolog_file_type(pl,       prolog).
1276user:prolog_file_type(prolog,   prolog).
1277user:prolog_file_type(qlf,      prolog).
1278user:prolog_file_type(qlf,      qlf).
1279user:prolog_file_type(Ext,      executable) :-
1280    current_prolog_flag(shared_object_extension, Ext).
1281user:prolog_file_type(dylib,    executable) :-
1282    current_prolog_flag(apple,  true).
1283
1284%!  '$chk_file'(+Spec, +Extensions, +Cond, +UseCache, -FullName)
1285%
1286%   File is a specification of a Prolog source file. Return the full
1287%   path of the file.
1288
1289'$chk_file'(Spec, _Extensions, _Cond, _Cache, _FullName) :-
1290    \+ ground(Spec),
1291    !,
1292    '$instantiation_error'(Spec).
1293'$chk_file'(Spec, Extensions, Cond, Cache, FullName) :-
1294    compound(Spec),
1295    functor(Spec, _, 1),
1296    !,
1297    '$relative_to'(Cond, cwd, CWD),
1298    '$chk_alias_file'(Spec, Extensions, Cond, Cache, CWD, FullName).
1299'$chk_file'(Segments, Ext, Cond, Cache, FullName) :-    % allow a/b/...
1300    \+ atomic(Segments),
1301    !,
1302    '$segments_to_atom'(Segments, Atom),
1303    '$chk_file'(Atom, Ext, Cond, Cache, FullName).
1304'$chk_file'(File, Exts, Cond, _, FullName) :-
1305    is_absolute_file_name(File),
1306    !,
1307    '$extend_file'(File, Exts, Extended),
1308    '$file_conditions'(Cond, Extended),
1309    '$absolute_file_name'(Extended, FullName).
1310'$chk_file'(File, Exts, Cond, _, FullName) :-
1311    '$relative_to'(Cond, source, Dir),
1312    atomic_list_concat([Dir, /, File], AbsFile),
1313    '$extend_file'(AbsFile, Exts, Extended),
1314    '$file_conditions'(Cond, Extended),
1315    !,
1316    '$absolute_file_name'(Extended, FullName).
1317'$chk_file'(File, Exts, Cond, _, FullName) :-
1318    '$extend_file'(File, Exts, Extended),
1319    '$file_conditions'(Cond, Extended),
1320    '$absolute_file_name'(Extended, FullName).
1321
1322'$segments_to_atom'(Atom, Atom) :-
1323    atomic(Atom),
1324    !.
1325'$segments_to_atom'(Segments, Atom) :-
1326    '$segments_to_list'(Segments, List, []),
1327    !,
1328    atomic_list_concat(List, /, Atom).
1329
1330'$segments_to_list'(A/B, H, T) :-
1331    '$segments_to_list'(A, H, T0),
1332    '$segments_to_list'(B, T0, T).
1333'$segments_to_list'(A, [A|T], T) :-
1334    atomic(A).
1335
1336
1337%!  '$relative_to'(+Condition, +Default, -Dir)
1338%
1339%   Determine the directory to work from.  This can be specified
1340%   explicitely using one or more relative_to(FileOrDir) options
1341%   or implicitely relative to the working directory or current
1342%   source-file.
1343
1344'$relative_to'(Conditions, Default, Dir) :-
1345    (   '$option'(relative_to(FileOrDir), Conditions)
1346    *-> (   exists_directory(FileOrDir)
1347        ->  Dir = FileOrDir
1348        ;   atom_concat(Dir, /, FileOrDir)
1349        ->  true
1350        ;   file_directory_name(FileOrDir, Dir)
1351        )
1352    ;   Default == cwd
1353    ->  '$cwd'(Dir)
1354    ;   Default == source
1355    ->  source_location(ContextFile, _Line),
1356        file_directory_name(ContextFile, Dir)
1357    ).
1358
1359%!  '$chk_alias_file'(+Spec, +Exts, +Cond, +Cache, +CWD,
1360%!                    -FullFile) is nondet.
1361
1362:- dynamic
1363    '$search_path_file_cache'/3,    % SHA1, Time, Path
1364    '$search_path_gc_time'/1.       % Time
1365:- volatile
1366    '$search_path_file_cache'/3,
1367    '$search_path_gc_time'/1.
1368
1369:- create_prolog_flag(file_search_cache_time, 10, []).
1370
1371'$chk_alias_file'(Spec, Exts, Cond, true, CWD, FullFile) :-
1372    !,
1373    findall(Exp, expand_file_search_path(Spec, Exp), Expansions),
1374    Cache = cache(Exts, Cond, CWD, Expansions),
1375    variant_sha1(Spec+Cache, SHA1),
1376    get_time(Now),
1377    current_prolog_flag(file_search_cache_time, TimeOut),
1378    (   '$search_path_file_cache'(SHA1, CachedTime, FullFile),
1379        CachedTime > Now - TimeOut,
1380        '$file_conditions'(Cond, FullFile)
1381    ->  '$search_message'(file_search(cache(Spec, Cond), FullFile))
1382    ;   '$member'(Expanded, Expansions),
1383        '$extend_file'(Expanded, Exts, LibFile),
1384        (   '$file_conditions'(Cond, LibFile),
1385            '$absolute_file_name'(LibFile, FullFile),
1386            '$cache_file_found'(SHA1, Now, TimeOut, FullFile)
1387        ->  '$search_message'(file_search(found(Spec, Cond), FullFile))
1388        ;   '$search_message'(file_search(tried(Spec, Cond), LibFile)),
1389            fail
1390        )
1391    ).
1392'$chk_alias_file'(Spec, Exts, Cond, false, _CWD, FullFile) :-
1393    expand_file_search_path(Spec, Expanded),
1394    '$extend_file'(Expanded, Exts, LibFile),
1395    '$file_conditions'(Cond, LibFile),
1396    '$absolute_file_name'(LibFile, FullFile).
1397
1398'$cache_file_found'(_, _, TimeOut, _) :-
1399    TimeOut =:= 0,
1400    !.
1401'$cache_file_found'(SHA1, Now, TimeOut, FullFile) :-
1402    '$search_path_file_cache'(SHA1, Saved, FullFile),
1403    !,
1404    (   Now - Saved < TimeOut/2
1405    ->  true
1406    ;   retractall('$search_path_file_cache'(SHA1, _, _)),
1407        asserta('$search_path_file_cache'(SHA1, Now, FullFile))
1408    ).
1409'$cache_file_found'(SHA1, Now, TimeOut, FullFile) :-
1410    'gc_file_search_cache'(TimeOut),
1411    asserta('$search_path_file_cache'(SHA1, Now, FullFile)).
1412
1413'gc_file_search_cache'(TimeOut) :-
1414    get_time(Now),
1415    '$search_path_gc_time'(Last),
1416    Now-Last < TimeOut/2,
1417    !.
1418'gc_file_search_cache'(TimeOut) :-
1419    get_time(Now),
1420    retractall('$search_path_gc_time'(_)),
1421    assertz('$search_path_gc_time'(Now)),
1422    Before is Now - TimeOut,
1423    (   '$search_path_file_cache'(SHA1, Cached, FullFile),
1424        Cached < Before,
1425        retractall('$search_path_file_cache'(SHA1, Cached, FullFile)),
1426        fail
1427    ;   true
1428    ).
1429
1430
1431'$search_message'(Term) :-
1432    current_prolog_flag(verbose_file_search, true),
1433    !,
1434    print_message(informational, Term).
1435'$search_message'(_).
1436
1437
1438%!  '$file_conditions'(+Condition, +Path)
1439%
1440%   Verify Path satisfies Condition.
1441
1442'$file_conditions'(List, File) :-
1443    is_list(List),
1444    !,
1445    \+ ( '$member'(C, List),
1446         '$file_condition'(C),
1447         \+ '$file_condition'(C, File)
1448       ).
1449'$file_conditions'(Map, File) :-
1450    \+ (  get_dict(Key, Map, Value),
1451          C =.. [Key,Value],
1452          '$file_condition'(C),
1453         \+ '$file_condition'(C, File)
1454       ).
1455
1456'$file_condition'(file_type(directory), File) :-
1457    !,
1458    exists_directory(File).
1459'$file_condition'(file_type(_), File) :-
1460    !,
1461    \+ exists_directory(File).
1462'$file_condition'(access(Accesses), File) :-
1463    !,
1464    \+ (  '$one_or_member'(Access, Accesses),
1465          \+ access_file(File, Access)
1466       ).
1467
1468'$file_condition'(exists).
1469'$file_condition'(file_type(_)).
1470'$file_condition'(access(_)).
1471
1472'$extend_file'(File, Exts, FileEx) :-
1473    '$ensure_extensions'(Exts, File, Fs),
1474    '$list_to_set'(Fs, FsSet),
1475    '$member'(FileEx, FsSet).
1476
1477'$ensure_extensions'([], _, []).
1478'$ensure_extensions'([E|E0], F, [FE|E1]) :-
1479    file_name_extension(F, E, FE),
1480    '$ensure_extensions'(E0, F, E1).
1481
1482%!  '$list_to_set'(+List, -Set) is det.
1483%
1484%   Turn list into a set, keeping   the  left-most copy of duplicate
1485%   elements.  Note  that  library(lists)  provides  an  O(N*log(N))
1486%   version, but sets of file name extensions should be short enough
1487%   for this not to matter.
1488
1489'$list_to_set'(List, Set) :-
1490    '$list_to_set'(List, [], Set).
1491
1492'$list_to_set'([], _, []).
1493'$list_to_set'([H|T], Seen, R) :-
1494    memberchk(H, Seen),
1495    !,
1496    '$list_to_set'(T, R).
1497'$list_to_set'([H|T], Seen, [H|R]) :-
1498    '$list_to_set'(T, [H|Seen], R).
1499
1500/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1501Canonicalise the extension list. Old SWI-Prolog   require  `.pl', etc, which
1502the Quintus compatibility  requests  `pl'.   This  layer  canonicalises  all
1503extensions to .ext
1504- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1505
1506'$canonicalise_extensions'([], []) :- !.
1507'$canonicalise_extensions'([H|T], [CH|CT]) :-
1508    !,
1509    '$must_be'(atom, H),
1510    '$canonicalise_extension'(H, CH),
1511    '$canonicalise_extensions'(T, CT).
1512'$canonicalise_extensions'(E, [CE]) :-
1513    '$canonicalise_extension'(E, CE).
1514
1515'$canonicalise_extension'('', '') :- !.
1516'$canonicalise_extension'(DotAtom, DotAtom) :-
1517    sub_atom(DotAtom, 0, _, _, '.'),
1518    !.
1519'$canonicalise_extension'(Atom, DotAtom) :-
1520    atom_concat('.', Atom, DotAtom).
1521
1522
1523                /********************************
1524                *            CONSULT            *
1525                *********************************/
1526
1527:- dynamic
1528    user:library_directory/1,
1529    user:prolog_load_file/2.
1530:- multifile
1531    user:library_directory/1,
1532    user:prolog_load_file/2.
1533
1534:- prompt(_, '|: ').
1535
1536:- thread_local
1537    '$compilation_mode_store'/1,    % database, wic, qlf
1538    '$directive_mode_store'/1.      % database, wic, qlf
1539:- volatile
1540    '$compilation_mode_store'/1,
1541    '$directive_mode_store'/1.
1542
1543'$compilation_mode'(Mode) :-
1544    (   '$compilation_mode_store'(Val)
1545    ->  Mode = Val
1546    ;   Mode = database
1547    ).
1548
1549'$set_compilation_mode'(Mode) :-
1550    retractall('$compilation_mode_store'(_)),
1551    assertz('$compilation_mode_store'(Mode)).
1552
1553'$compilation_mode'(Old, New) :-
1554    '$compilation_mode'(Old),
1555    (   New == Old
1556    ->  true
1557    ;   '$set_compilation_mode'(New)
1558    ).
1559
1560'$directive_mode'(Mode) :-
1561    (   '$directive_mode_store'(Val)
1562    ->  Mode = Val
1563    ;   Mode = database
1564    ).
1565
1566'$directive_mode'(Old, New) :-
1567    '$directive_mode'(Old),
1568    (   New == Old
1569    ->  true
1570    ;   '$set_directive_mode'(New)
1571    ).
1572
1573'$set_directive_mode'(Mode) :-
1574    retractall('$directive_mode_store'(_)),
1575    assertz('$directive_mode_store'(Mode)).
1576
1577
1578%!  '$compilation_level'(-Level) is det.
1579%
1580%   True when Level reflects the nesting   in  files compiling other
1581%   files. 0 if no files are being loaded.
1582
1583'$compilation_level'(Level) :-
1584    '$input_context'(Stack),
1585    '$compilation_level'(Stack, Level).
1586
1587'$compilation_level'([], 0).
1588'$compilation_level'([Input|T], Level) :-
1589    (   arg(1, Input, see)
1590    ->  '$compilation_level'(T, Level)
1591    ;   '$compilation_level'(T, Level0),
1592        Level is Level0+1
1593    ).
1594
1595
1596%!  compiling
1597%
1598%   Is true if SWI-Prolog is generating a state or qlf file or
1599%   executes a `call' directive while doing this.
1600
1601compiling :-
1602    \+ (   '$compilation_mode'(database),
1603           '$directive_mode'(database)
1604       ).
1605
1606:- meta_predicate
1607    '$ifcompiling'(0).
1608
1609'$ifcompiling'(G) :-
1610    (   '$compilation_mode'(database)
1611    ->  true
1612    ;   call(G)
1613    ).
1614
1615                /********************************
1616                *         READ SOURCE           *
1617                *********************************/
1618
1619%!  '$load_msg_level'(+Action, +NestingLevel, -StartVerbose, -EndVerbose)
1620
1621'$load_msg_level'(Action, Nesting, Start, Done) :-
1622    '$update_autoload_level'([], 0),
1623    !,
1624    current_prolog_flag(verbose_load, Type0),
1625    '$load_msg_compat'(Type0, Type),
1626    (   '$load_msg_level'(Action, Nesting, Type, Start, Done)
1627    ->  true
1628    ).
1629'$load_msg_level'(_, _, silent, silent).
1630
1631'$load_msg_compat'(true, normal) :- !.
1632'$load_msg_compat'(false, silent) :- !.
1633'$load_msg_compat'(X, X).
1634
1635'$load_msg_level'(load_file,    _, full,   informational, informational).
1636'$load_msg_level'(include_file, _, full,   informational, informational).
1637'$load_msg_level'(load_file,    _, normal, silent,        informational).
1638'$load_msg_level'(include_file, _, normal, silent,        silent).
1639'$load_msg_level'(load_file,    0, brief,  silent,        informational).
1640'$load_msg_level'(load_file,    _, brief,  silent,        silent).
1641'$load_msg_level'(include_file, _, brief,  silent,        silent).
1642'$load_msg_level'(load_file,    _, silent, silent,        silent).
1643'$load_msg_level'(include_file, _, silent, silent,        silent).
1644
1645%!  '$source_term'(+From, -Read, -RLayout, -Term, -TLayout,
1646%!                 -Stream, +Options) is nondet.
1647%
1648%   Read Prolog terms from the  input   From.  Terms are returned on
1649%   backtracking. Associated resources (i.e.,   streams)  are closed
1650%   due to setup_call_cleanup/3.
1651%
1652%   @param From is either a term stream(Id, Stream) or a file
1653%          specification.
1654%   @param Read is the raw term as read from the input.
1655%   @param Term is the term after term-expansion.  If a term is
1656%          expanded into the empty list, this is returned too.  This
1657%          is required to be able to return the raw term in Read
1658%   @param Stream is the stream from which Read is read
1659%   @param Options provides additional options:
1660%           * encoding(Enc)
1661%           Encoding used to open From
1662%           * syntax_errors(+ErrorMode)
1663%           * process_comments(+Boolean)
1664%           * term_position(-Pos)
1665
1666'$source_term'(From, Read, RLayout, Term, TLayout, Stream, Options) :-
1667    '$source_term'(From, Read, RLayout, Term, TLayout, Stream, [], Options),
1668    (   Term == end_of_file
1669    ->  !, fail
1670    ;   Term \== begin_of_file
1671    ).
1672
1673'$source_term'(Input, _,_,_,_,_,_,_) :-
1674    \+ ground(Input),
1675    !,
1676    '$instantiation_error'(Input).
1677'$source_term'(stream(Id, In, Opts),
1678               Read, RLayout, Term, TLayout, Stream, Parents, Options) :-
1679    !,
1680    '$record_included'(Parents, Id, Id, 0.0, Message),
1681    setup_call_cleanup(
1682        '$open_source'(stream(Id, In, Opts), In, State, Parents, Options),
1683        '$term_in_file'(In, Read, RLayout, Term, TLayout, Stream,
1684                        [Id|Parents], Options),
1685        '$close_source'(State, Message)).
1686'$source_term'(File,
1687               Read, RLayout, Term, TLayout, Stream, Parents, Options) :-
1688    absolute_file_name(File, Path,
1689                       [ file_type(prolog),
1690                         access(read)
1691                       ]),
1692    time_file(Path, Time),
1693    '$record_included'(Parents, File, Path, Time, Message),
1694    setup_call_cleanup(
1695        '$open_source'(Path, In, State, Parents, Options),
1696        '$term_in_file'(In, Read, RLayout, Term, TLayout, Stream,
1697                        [Path|Parents], Options),
1698        '$close_source'(State, Message)).
1699
1700:- thread_local
1701    '$load_input'/2.
1702:- volatile
1703    '$load_input'/2.
1704
1705'$open_source'(stream(Id, In, Opts), In,
1706               restore(In, StreamState, Id, Ref, Opts), Parents, _Options) :-
1707    !,
1708    '$context_type'(Parents, ContextType),
1709    '$push_input_context'(ContextType),
1710    '$prepare_load_stream'(In, Id, StreamState),
1711    asserta('$load_input'(stream(Id), In), Ref).
1712'$open_source'(Path, In, close(In, Path, Ref), Parents, Options) :-
1713    '$context_type'(Parents, ContextType),
1714    '$push_input_context'(ContextType),
1715    '$open_source'(Path, In, Options),
1716    '$set_encoding'(In, Options),
1717    asserta('$load_input'(Path, In), Ref).
1718
1719'$context_type'([], load_file) :- !.
1720'$context_type'(_, include).
1721
1722:- multifile prolog:open_source_hook/3.
1723
1724'$open_source'(Path, In, Options) :-
1725    prolog:open_source_hook(Path, In, Options),
1726    !.
1727'$open_source'(Path, In, _Options) :-
1728    open(Path, read, In).
1729
1730'$close_source'(close(In, _Id, Ref), Message) :-
1731    erase(Ref),
1732    call_cleanup(
1733        close(In),
1734        '$pop_input_context'),
1735    '$close_message'(Message).
1736'$close_source'(restore(In, StreamState, _Id, Ref, Opts), Message) :-
1737    erase(Ref),
1738    call_cleanup(
1739        '$restore_load_stream'(In, StreamState, Opts),
1740        '$pop_input_context'),
1741    '$close_message'(Message).
1742
1743'$close_message'(message(Level, Msg)) :-
1744    !,
1745    '$print_message'(Level, Msg).
1746'$close_message'(_).
1747
1748
1749%!  '$term_in_file'(+In, -Read, -RLayout, -Term, -TLayout,
1750%!                  -Stream, +Parents, +Options) is multi.
1751%
1752%   True when Term is an expanded term from   In. Read is a raw term
1753%   (before term-expansion). Stream is  the   actual  stream,  which
1754%   starts at In, but may change due to processing included files.
1755%
1756%   @see '$source_term'/8 for details.
1757
1758'$term_in_file'(In, Read, RLayout, Term, TLayout, Stream, Parents, Options) :-
1759    Parents \= [_,_|_],
1760    (   '$load_input'(_, Input)
1761    ->  stream_property(Input, file_name(File))
1762    ),
1763    '$set_source_location'(File, 0),
1764    '$expanded_term'(In,
1765                     begin_of_file, 0-0, Read, RLayout, Term, TLayout,
1766                     Stream, Parents, Options).
1767'$term_in_file'(In, Read, RLayout, Term, TLayout, Stream, Parents, Options) :-
1768    '$skip_script_line'(In, Options),
1769    '$read_clause_options'(Options, ReadOptions),
1770    repeat,
1771      read_clause(In, Raw,
1772                  [ variable_names(Bindings),
1773                    term_position(Pos),
1774                    subterm_positions(RawLayout)
1775                  | ReadOptions
1776                  ]),
1777      b_setval('$term_position', Pos),
1778      b_setval('$variable_names', Bindings),
1779      (   Raw == end_of_file
1780      ->  !,
1781          (   Parents = [_,_|_]     % Included file
1782          ->  fail
1783          ;   '$expanded_term'(In,
1784                               Raw, RawLayout, Read, RLayout, Term, TLayout,
1785                               Stream, Parents, Options)
1786          )
1787      ;   '$expanded_term'(In, Raw, RawLayout, Read, RLayout, Term, TLayout,
1788                           Stream, Parents, Options)
1789      ).
1790
1791'$read_clause_options'([], []).
1792'$read_clause_options'([H|T0], List) :-
1793    (   '$read_clause_option'(H)
1794    ->  List = [H|T]
1795    ;   List = T
1796    ),
1797    '$read_clause_options'(T0, T).
1798
1799'$read_clause_option'(syntax_errors(_)).
1800'$read_clause_option'(term_position(_)).
1801'$read_clause_option'(process_comment(_)).
1802
1803'$expanded_term'(In, Raw, RawLayout, Read, RLayout, Term, TLayout,
1804                 Stream, Parents, Options) :-
1805    E = error(_,_),
1806    catch('$expand_term'(Raw, RawLayout, Expanded, ExpandedLayout), E,
1807          '$print_message_fail'(E)),
1808    (   Expanded \== []
1809    ->  '$expansion_member'(Expanded, ExpandedLayout, Term1, Layout1)
1810    ;   Term1 = Expanded,
1811        Layout1 = ExpandedLayout
1812    ),
1813    (   nonvar(Term1), Term1 = (:-Directive), nonvar(Directive)
1814    ->  (   Directive = include(File),
1815            '$current_source_module'(Module),
1816            '$valid_directive'(Module:include(File))
1817        ->  stream_property(In, encoding(Enc)),
1818            '$add_encoding'(Enc, Options, Options1),
1819            '$source_term'(File, Read, RLayout, Term, TLayout,
1820                           Stream, Parents, Options1)
1821        ;   Directive = encoding(Enc)
1822        ->  set_stream(In, encoding(Enc)),
1823            fail
1824        ;   Term = Term1,
1825            Stream = In,
1826            Read = Raw
1827        )
1828    ;   Term = Term1,
1829        TLayout = Layout1,
1830        Stream = In,
1831        Read = Raw,
1832        RLayout = RawLayout
1833    ).
1834
1835'$expansion_member'(Var, Layout, Var, Layout) :-
1836    var(Var),
1837    !.
1838'$expansion_member'([], _, _, _) :- !, fail.
1839'$expansion_member'(List, ListLayout, Term, Layout) :-
1840    is_list(List),
1841    !,
1842    (   var(ListLayout)
1843    ->  '$member'(Term, List)
1844    ;   is_list(ListLayout)
1845    ->  '$member_rep2'(Term, Layout, List, ListLayout)
1846    ;   Layout = ListLayout,
1847        '$member'(Term, List)
1848    ).
1849'$expansion_member'(X, Layout, X, Layout).
1850
1851% pairwise member, repeating last element of the second
1852% list.
1853
1854'$member_rep2'(H1, H2, [H1|_], [H2|_]).
1855'$member_rep2'(H1, H2, [_|T1], [T2]) :-
1856    !,
1857    '$member_rep2'(H1, H2, T1, [T2]).
1858'$member_rep2'(H1, H2, [_|T1], [_|T2]) :-
1859    '$member_rep2'(H1, H2, T1, T2).
1860
1861%!  '$add_encoding'(+Enc, +Options0, -Options)
1862
1863'$add_encoding'(Enc, Options0, Options) :-
1864    (   Options0 = [encoding(Enc)|_]
1865    ->  Options = Options0
1866    ;   Options = [encoding(Enc)|Options0]
1867    ).
1868
1869
1870:- multifile
1871    '$included'/4.                  % Into, Line, File, LastModified
1872:- dynamic
1873    '$included'/4.
1874
1875%!  '$record_included'(+Parents, +File, +Path, +Time, -Message) is det.
1876%
1877%   Record that we included File into the   head of Parents. This is
1878%   troublesome when creating a QLF  file   because  this may happen
1879%   before we opened the QLF file (and  we   do  not yet know how to
1880%   open the file because we  do  not   yet  know  whether this is a
1881%   module file or not).
1882%
1883%   I think that the only sensible  solution   is  to have a special
1884%   statement for this, that may appear  both inside and outside QLF
1885%   `parts'.
1886
1887'$record_included'([Parent|Parents], File, Path, Time,
1888                   message(DoneMsgLevel,
1889                           include_file(done(Level, file(File, Path))))) :-
1890    source_location(SrcFile, Line),
1891    !,
1892    '$compilation_level'(Level),
1893    '$load_msg_level'(include_file, Level, StartMsgLevel, DoneMsgLevel),
1894    '$print_message'(StartMsgLevel,
1895                     include_file(start(Level,
1896                                        file(File, Path)))),
1897    '$last'([Parent|Parents], Owner),
1898    (   (   '$compilation_mode'(database)
1899        ;   '$qlf_current_source'(Owner)
1900        )
1901    ->  '$store_admin_clause'(
1902            system:'$included'(Parent, Line, Path, Time),
1903            _, Owner, SrcFile:Line)
1904    ;   '$qlf_include'(Owner, Parent, Line, Path, Time)
1905    ).
1906'$record_included'(_, _, _, _, true).
1907
1908%!  '$master_file'(+File, -MasterFile)
1909%
1910%   Find the primary load file from included files.
1911
1912'$master_file'(File, MasterFile) :-
1913    '$included'(MasterFile0, _Line, File, _Time),
1914    !,
1915    '$master_file'(MasterFile0, MasterFile).
1916'$master_file'(File, File).
1917
1918
1919'$skip_script_line'(_In, Options) :-
1920    '$option'(check_script(false), Options),
1921    !.
1922'$skip_script_line'(In, _Options) :-
1923    (   peek_char(In, #)
1924    ->  skip(In, 10)
1925    ;   true
1926    ).
1927
1928'$set_encoding'(Stream, Options) :-
1929    '$option'(encoding(Enc), Options),
1930    !,
1931    Enc \== default,
1932    set_stream(Stream, encoding(Enc)).
1933'$set_encoding'(_, _).
1934
1935
1936'$prepare_load_stream'(In, Id, state(HasName,HasPos)) :-
1937    (   stream_property(In, file_name(_))
1938    ->  HasName = true,
1939        (   stream_property(In, position(_))
1940        ->  HasPos = true
1941        ;   HasPos = false,
1942            set_stream(In, record_position(true))
1943        )
1944    ;   HasName = false,
1945        set_stream(In, file_name(Id)),
1946        (   stream_property(In, position(_))
1947        ->  HasPos = true
1948        ;   HasPos = false,
1949            set_stream(In, record_position(true))
1950        )
1951    ).
1952
1953'$restore_load_stream'(In, _State, Options) :-
1954    memberchk(close(true), Options),
1955    !,
1956    close(In).
1957'$restore_load_stream'(In, state(HasName, HasPos), _Options) :-
1958    (   HasName == false
1959    ->  set_stream(In, file_name(''))
1960    ;   true
1961    ),
1962    (   HasPos == false
1963    ->  set_stream(In, record_position(false))
1964    ;   true
1965    ).
1966
1967
1968                 /*******************************
1969                 *          DERIVED FILES       *
1970                 *******************************/
1971
1972:- dynamic
1973    '$derived_source_db'/3.         % Loaded, DerivedFrom, Time
1974
1975'$register_derived_source'(_, '-') :- !.
1976'$register_derived_source'(Loaded, DerivedFrom) :-
1977    retractall('$derived_source_db'(Loaded, _, _)),
1978    time_file(DerivedFrom, Time),
1979    assert('$derived_source_db'(Loaded, DerivedFrom, Time)).
1980
1981%       Auto-importing dynamic predicates is not very elegant and
1982%       leads to problems with qsave_program/[1,2]
1983
1984'$derived_source'(Loaded, DerivedFrom, Time) :-
1985    '$derived_source_db'(Loaded, DerivedFrom, Time).
1986
1987
1988                /********************************
1989                *       LOAD PREDICATES         *
1990                *********************************/
1991
1992:- meta_predicate
1993    ensure_loaded(:),
1994    [:|+],
1995    consult(:),
1996    use_module(:),
1997    use_module(:, +),
1998    reexport(:),
1999    reexport(:, +),
2000    load_files(:),
2001    load_files(:, +).
2002
2003%!  ensure_loaded(+FileOrListOfFiles)
2004%
2005%   Load specified files, provided they where not loaded before. If the
2006%   file is a module file import the public predicates into the context
2007%   module.
2008
2009ensure_loaded(Files) :-
2010    load_files(Files, [if(not_loaded)]).
2011
2012%!  use_module(+FileOrListOfFiles)
2013%
2014%   Very similar to ensure_loaded/1, but insists on the loaded file to
2015%   be a module file. If the file is already imported, but the public
2016%   predicates are not yet imported into the context module, then do
2017%   so.
2018
2019use_module(Files) :-
2020    load_files(Files, [ if(not_loaded),
2021                        must_be_module(true)
2022                      ]).
2023
2024%!  use_module(+File, +ImportList)
2025%
2026%   As use_module/1, but takes only one file argument and imports only
2027%   the specified predicates rather than all public predicates.
2028
2029use_module(File, Import) :-
2030    load_files(File, [ if(not_loaded),
2031                       must_be_module(true),
2032                       imports(Import)
2033                     ]).
2034
2035%!  reexport(+Files)
2036%
2037%   As use_module/1, exporting all imported predicates.
2038
2039reexport(Files) :-
2040    load_files(Files, [ if(not_loaded),
2041                        must_be_module(true),
2042                        reexport(true)
2043                      ]).
2044
2045%!  reexport(+File, +ImportList)
2046%
2047%   As use_module/1, re-exporting all imported predicates.
2048
2049reexport(File, Import) :-
2050    load_files(File, [ if(not_loaded),
2051                       must_be_module(true),
2052                       imports(Import),
2053                       reexport(true)
2054                     ]).
2055
2056
2057[X] :-
2058    !,
2059    consult(X).
2060[M:F|R] :-
2061    consult(M:[F|R]).
2062
2063consult(M:X) :-
2064    X == user,
2065    !,
2066    flag('$user_consult', N, N+1),
2067    NN is N + 1,
2068    atom_concat('user://', NN, Id),
2069    load_files(M:Id, [stream(user_input), check_script(false), silent(false)]).
2070consult(List) :-
2071    load_files(List, [expand(true)]).
2072
2073%!  load_files(:File, +Options)
2074%
2075%   Common entry for all the consult derivates.  File is the raw user
2076%   specified file specification, possibly tagged with the module.
2077
2078load_files(Files) :-
2079    load_files(Files, []).
2080load_files(Module:Files, Options) :-
2081    '$must_be'(list, Options),
2082    '$load_files'(Files, Module, Options).
2083
2084'$load_files'(X, _, _) :-
2085    var(X),
2086    !,
2087    '$instantiation_error'(X).
2088'$load_files'([], _, _) :- !.
2089'$load_files'(Id, Module, Options) :-   % load_files(foo, [stream(In)])
2090    '$option'(stream(_), Options),
2091    !,
2092    (   atom(Id)
2093    ->  '$load_file'(Id, Module, Options)
2094    ;   throw(error(type_error(atom, Id), _))
2095    ).
2096'$load_files'(List, Module, Options) :-
2097    List = [_|_],
2098    !,
2099    '$must_be'(list, List),
2100    '$load_file_list'(List, Module, Options).
2101'$load_files'(File, Module, Options) :-
2102    '$load_one_file'(File, Module, Options).
2103
2104'$load_file_list'([], _, _).
2105'$load_file_list'([File|Rest], Module, Options) :-
2106    E = error(_,_),
2107    catch('$load_one_file'(File, Module, Options), E,
2108          '$print_message'(error, E)),
2109    '$load_file_list'(Rest, Module, Options).
2110
2111
2112'$load_one_file'(Spec, Module, Options) :-
2113    atomic(Spec),
2114    '$option'(expand(Expand), Options, false),
2115    Expand == true,
2116    !,
2117    expand_file_name(Spec, Expanded),
2118    (   Expanded = [Load]
2119    ->  true
2120    ;   Load = Expanded
2121    ),
2122    '$load_files'(Load, Module, [expand(false)|Options]).
2123'$load_one_file'(File, Module, Options) :-
2124    strip_module(Module:File, Into, PlainFile),
2125    '$load_file'(PlainFile, Into, Options).
2126
2127
2128%!  '$noload'(+Condition, +FullFile, +Options) is semidet.
2129%
2130%   True of FullFile should _not_ be loaded.
2131
2132'$noload'(true, _, _) :-
2133    !,
2134    fail.
2135'$noload'(_, FullFile, _Options) :-
2136    '$time_source_file'(FullFile, Time, system),
2137    Time > 0.0,
2138    !.
2139'$noload'(not_loaded, FullFile, _) :-
2140    source_file(FullFile),
2141    !.
2142'$noload'(changed, Derived, _) :-
2143    '$derived_source'(_FullFile, Derived, LoadTime),
2144    time_file(Derived, Modified),
2145    Modified @=< LoadTime,
2146    !.
2147'$noload'(changed, FullFile, Options) :-
2148    '$time_source_file'(FullFile, LoadTime, user),
2149    '$modified_id'(FullFile, Modified, Options),
2150    Modified @=< LoadTime,
2151    !.
2152
2153%!  '$qlf_file'(+Spec, +PlFile, -LoadFile, -Mode, +Options) is det.
2154%
2155%   Determine how to load the source. LoadFile is the file to be loaded,
2156%   Mode is how to load it. Mode is one of
2157%
2158%     - compile
2159%     Normal source compilation
2160%     - qcompile
2161%     Compile from source, creating a QLF file in the process
2162%     - qload
2163%     Load from QLF file.
2164%     - stream
2165%     Load from a stream.  Content can be a source or QLF file.
2166%
2167%   @arg Spec is the original search specification
2168%   @arg PlFile is the resolved absolute path to the Prolog file.
2169
2170'$qlf_file'(Spec, _, Spec, stream, Options) :-
2171    '$option'(stream(_), Options),      % stream: no choice
2172    !.
2173'$qlf_file'(Spec, FullFile, FullFile, compile, _) :-
2174    '$spec_extension'(Spec, Ext),       % user explicitly specified
2175    user:prolog_file_type(Ext, prolog),
2176    !.
2177'$qlf_file'(Spec, FullFile, LoadFile, Mode, Options) :-
2178    '$compilation_mode'(database),
2179    file_name_extension(Base, PlExt, FullFile),
2180    user:prolog_file_type(PlExt, prolog),
2181    user:prolog_file_type(QlfExt, qlf),
2182    file_name_extension(Base, QlfExt, QlfFile),
2183    (   access_file(QlfFile, read),
2184        (   '$qlf_out_of_date'(FullFile, QlfFile, Why)
2185        ->  (   access_file(QlfFile, write)
2186            ->  print_message(informational,
2187                              qlf(recompile(Spec, FullFile, QlfFile, Why))),
2188                Mode = qcompile,
2189                LoadFile = FullFile
2190            ;   Why == old,
2191                current_prolog_flag(home, PlHome),
2192                sub_atom(FullFile, 0, _, _, PlHome)
2193            ->  print_message(silent,
2194                              qlf(system_lib_out_of_date(Spec, QlfFile))),
2195                Mode = qload,
2196                LoadFile = QlfFile
2197            ;   print_message(warning,
2198                              qlf(can_not_recompile(Spec, QlfFile, Why))),
2199                Mode = compile,
2200                LoadFile = FullFile
2201            )
2202        ;   Mode = qload,
2203            LoadFile = QlfFile
2204        )
2205    ->  !
2206    ;   '$qlf_auto'(FullFile, QlfFile, Options)
2207    ->  !, Mode = qcompile,
2208        LoadFile = FullFile
2209    ).
2210'$qlf_file'(_, FullFile, FullFile, compile, _).
2211
2212
2213%!  '$qlf_out_of_date'(+PlFile, +QlfFile, -Why) is semidet.
2214%
2215%   True if the  QlfFile  file  is   out-of-date  because  of  Why. This
2216%   predicate is the negation such that we can return the reason.
2217
2218'$qlf_out_of_date'(PlFile, QlfFile, Why) :-
2219    (   access_file(PlFile, read)
2220    ->  time_file(PlFile, PlTime),
2221        time_file(QlfFile, QlfTime),
2222        (   PlTime > QlfTime
2223        ->  Why = old                   % PlFile is newer
2224        ;   Error = error(Formal,_),
2225            catch('$qlf_sources'(QlfFile, _Files), Error, true),
2226            nonvar(Formal)              % QlfFile is incompatible
2227        ->  Why = Error
2228        ;   fail                        % QlfFile is up-to-date and ok
2229        )
2230    ;   fail                            % can not read .pl; try .qlf
2231    ).
2232
2233%!  '$qlf_auto'(+PlFile, +QlfFile, +Options) is semidet.
2234%
2235%   True if we create QlfFile using   qcompile/2. This is determined
2236%   by the option qcompile(QlfMode) or, if   this is not present, by
2237%   the prolog_flag qcompile.
2238
2239:- create_prolog_flag(qcompile, false, [type(atom)]).
2240
2241'$qlf_auto'(PlFile, QlfFile, Options) :-
2242    (   memberchk(qcompile(QlfMode), Options)
2243    ->  true
2244    ;   current_prolog_flag(qcompile, QlfMode),
2245        \+ '$in_system_dir'(PlFile)
2246    ),
2247    (   QlfMode == auto
2248    ->  true
2249    ;   QlfMode == large,
2250        size_file(PlFile, Size),
2251        Size > 100000
2252    ),
2253    access_file(QlfFile, write).
2254
2255'$in_system_dir'(PlFile) :-
2256    current_prolog_flag(home, Home),
2257    sub_atom(PlFile, 0, _, _, Home).
2258
2259'$spec_extension'(File, Ext) :-
2260    atom(File),
2261    file_name_extension(_, Ext, File).
2262'$spec_extension'(Spec, Ext) :-
2263    compound(Spec),
2264    arg(1, Spec, Arg),
2265    '$spec_extension'(Arg, Ext).
2266
2267
2268%!  '$load_file'(+Spec, +ContextModule, +Options) is det.
2269%
2270%   Load the file Spec  into   ContextModule  controlled by Options.
2271%   This wrapper deals with two cases  before proceeding to the real
2272%   loader:
2273%
2274%       * User hooks based on prolog_load_file/2
2275%       * The file is already loaded.
2276
2277:- dynamic
2278    '$resolved_source_path'/2.                  % ?Spec, ?Path
2279
2280'$load_file'(File, Module, Options) :-
2281    \+ memberchk(stream(_), Options),
2282    user:prolog_load_file(Module:File, Options),
2283    !.
2284'$load_file'(File, Module, Options) :-
2285    memberchk(stream(_), Options),
2286    !,
2287    '$assert_load_context_module'(File, Module, Options),
2288    '$qdo_load_file'(File, File, Module, Options).
2289'$load_file'(File, Module, Options) :-
2290    (   '$resolved_source_path'(File, FullFile, Options)
2291    ->  true
2292    ;   '$resolve_source_path'(File, FullFile, Options)
2293    ),
2294    '$mt_load_file'(File, FullFile, Module, Options).
2295
2296%!  '$resolved_source_path'(+File, -FullFile, +Options) is semidet.
2297%
2298%   True when File has already been resolved to an absolute path.
2299
2300'$resolved_source_path'(File, FullFile, Options) :-
2301    '$resolved_source_path'(File, FullFile),
2302    (   '$source_file_property'(FullFile, from_state, true)
2303    ;   '$source_file_property'(FullFile, resource, true)
2304    ;   '$option'(if(If), Options, true),
2305        '$noload'(If, FullFile, Options)
2306    ),
2307    !.
2308
2309%!  '$resolve_source_path'(+File, -FullFile, Options) is det.
2310%
2311%   Resolve a source file specification to   an absolute path. May throw
2312%   existence and other errors.
2313
2314'$resolve_source_path'(File, FullFile, _Options) :-
2315    absolute_file_name(File, FullFile,
2316                       [ file_type(prolog),
2317                         access(read)
2318                       ]),
2319    '$register_resolved_source_path'(File, FullFile).
2320
2321
2322'$register_resolved_source_path'(File, FullFile) :-
2323    '$resolved_source_path'(File, FullFile),
2324    !.
2325'$register_resolved_source_path'(File, FullFile) :-
2326    compound(File),
2327    !,
2328    asserta('$resolved_source_path'(File, FullFile)).
2329'$register_resolved_source_path'(_, _).
2330
2331%!  '$translated_source'(+Old, +New) is det.
2332%
2333%   Called from loading a QLF state when source files are being renamed.
2334
2335:- public '$translated_source'/2.
2336'$translated_source'(Old, New) :-
2337    forall(retract('$resolved_source_path'(File, Old)),
2338           assertz('$resolved_source_path'(File, New))).
2339
2340%!  '$register_resource_file'(+FullFile) is det.
2341%
2342%   If we load a file from a resource we   lock  it, so we never have to
2343%   check the modification again.
2344
2345'$register_resource_file'(FullFile) :-
2346    (   sub_atom(FullFile, 0, _, _, 'res://')
2347    ->  '$set_source_file'(FullFile, resource, true)
2348    ;   true
2349    ).
2350
2351%!  '$already_loaded'(+File, +FullFile, +Module, +Options) is det.
2352%
2353%   Called if File is already loaded. If  this is a module-file, the
2354%   module must be imported into the context  Module. If it is not a
2355%   module file, it must be reloaded.
2356%
2357%   @bug    A file may be associated with multiple modules.  How
2358%           do we find the `main export module'?  Currently there
2359%           is no good way to find out which module is associated
2360%           to the file as a result of the first :- module/2 term.
2361
2362'$already_loaded'(_File, FullFile, Module, Options) :-
2363    '$assert_load_context_module'(FullFile, Module, Options),
2364    '$current_module'(LoadModules, FullFile),
2365    !,
2366    (   atom(LoadModules)
2367    ->  LoadModule = LoadModules
2368    ;   LoadModules = [LoadModule|_]
2369    ),
2370    '$import_from_loaded_module'(LoadModule, Module, Options).
2371'$already_loaded'(_, _, user, _) :- !.
2372'$already_loaded'(File, FullFile, Module, Options) :-
2373    (   '$load_context_module'(FullFile, Module, CtxOptions),
2374        '$load_ctx_options'(Options, CtxOptions)
2375    ->  true
2376    ;   '$load_file'(File, Module, [if(true)|Options])
2377    ).
2378
2379%!  '$mt_load_file'(+File, +FullFile, +Module, +Options) is det.
2380%
2381%   Deal with multi-threaded  loading  of   files.  The  thread that
2382%   wishes to load the thread first will  do so, while other threads
2383%   will wait until the leader finished and  than act as if the file
2384%   is already loaded.
2385%
2386%   Synchronisation is handled using  a   message  queue that exists
2387%   while the file is being loaded.   This synchronisation relies on
2388%   the fact that thread_get_message/1 throws  an existence_error if
2389%   the message queue  is  destroyed.  This   is  hacky.  Events  or
2390%   condition variables would have made a cleaner design.
2391
2392:- dynamic
2393    '$loading_file'/3.              % File, Queue, Thread
2394:- volatile
2395    '$loading_file'/3.
2396
2397'$mt_load_file'(File, FullFile, Module, Options) :-
2398    current_prolog_flag(threads, true),
2399    !,
2400    setup_call_cleanup(
2401        with_mutex('$load_file',
2402                   '$mt_start_load'(FullFile, Loading, Options)),
2403        '$mt_do_load'(Loading, File, FullFile, Module, Options),
2404        '$mt_end_load'(Loading)).
2405'$mt_load_file'(File, FullFile, Module, Options) :-
2406    '$option'(if(If), Options, true),
2407    '$noload'(If, FullFile, Options),
2408    !,
2409    '$already_loaded'(File, FullFile, Module, Options).
2410'$mt_load_file'(File, FullFile, Module, Options) :-
2411    '$qdo_load_file'(File, FullFile, Module, Options).
2412
2413'$mt_start_load'(FullFile, queue(Queue), _) :-
2414    '$loading_file'(FullFile, Queue, LoadThread),
2415    \+ thread_self(LoadThread),
2416    !.
2417'$mt_start_load'(FullFile, already_loaded, Options) :-
2418    '$option'(if(If), Options, true),
2419    '$noload'(If, FullFile, Options),
2420    !.
2421'$mt_start_load'(FullFile, Ref, _) :-
2422    thread_self(Me),
2423    message_queue_create(Queue),
2424    assertz('$loading_file'(FullFile, Queue, Me), Ref).
2425
2426'$mt_do_load'(queue(Queue), File, FullFile, Module, Options) :-
2427    !,
2428    catch(thread_get_message(Queue, _), error(_,_), true),
2429    '$already_loaded'(File, FullFile, Module, Options).
2430'$mt_do_load'(already_loaded, File, FullFile, Module, Options) :-
2431    !,
2432    '$already_loaded'(File, FullFile, Module, Options).
2433'$mt_do_load'(_Ref, File, FullFile, Module, Options) :-
2434    '$assert_load_context_module'(FullFile, Module, Options),
2435    '$qdo_load_file'(File, FullFile, Module, Options).
2436
2437'$mt_end_load'(queue(_)) :- !.
2438'$mt_end_load'(already_loaded) :- !.
2439'$mt_end_load'(Ref) :-
2440    clause('$loading_file'(_, Queue, _), _, Ref),
2441    erase(Ref),
2442    thread_send_message(Queue, done),
2443    message_queue_destroy(Queue).
2444
2445
2446%!  '$qdo_load_file'(+Spec, +FullFile, +ContextModule, +Options) is det.
2447%
2448%   Switch to qcompile mode if requested by the option '$qlf'(+Out)
2449
2450'$qdo_load_file'(File, FullFile, Module, Options) :-
2451    '$qdo_load_file2'(File, FullFile, Module, Action, Options),
2452    '$register_resource_file'(FullFile),
2453    '$run_initialization'(FullFile, Action, Options).
2454
2455'$qdo_load_file2'(File, FullFile, Module, Action, Options) :-
2456    memberchk('$qlf'(QlfOut), Options),
2457    '$stage_file'(QlfOut, StageQlf),
2458    !,
2459    setup_call_catcher_cleanup(
2460        '$qstart'(StageQlf, Module, State),
2461        '$do_load_file'(File, FullFile, Module, Action, Options),
2462        Catcher,
2463        '$qend'(State, Catcher, StageQlf, QlfOut)).
2464'$qdo_load_file2'(File, FullFile, Module, Action, Options) :-
2465    '$do_load_file'(File, FullFile, Module, Action, Options).
2466
2467'$qstart'(Qlf, Module, state(OldMode, OldModule)) :-
2468    '$qlf_open'(Qlf),
2469    '$compilation_mode'(OldMode, qlf),
2470    '$set_source_module'(OldModule, Module).
2471
2472'$qend'(state(OldMode, OldModule), Catcher, StageQlf, QlfOut) :-
2473    '$set_source_module'(_, OldModule),
2474    '$set_compilation_mode'(OldMode),
2475    '$qlf_close',
2476    '$install_staged_file'(Catcher, StageQlf, QlfOut, warn).
2477
2478'$set_source_module'(OldModule, Module) :-
2479    '$current_source_module'(OldModule),
2480    '$set_source_module'(Module).
2481
2482%!  '$do_load_file'(+Spec, +FullFile, +ContextModule,
2483%!                  -Action, +Options) is det.
2484%
2485%   Perform the actual loading.
2486
2487'$do_load_file'(File, FullFile, Module, Action, Options) :-
2488    '$option'(derived_from(DerivedFrom), Options, -),
2489    '$register_derived_source'(FullFile, DerivedFrom),
2490    '$qlf_file'(File, FullFile, Absolute, Mode, Options),
2491    (   Mode == qcompile
2492    ->  qcompile(Module:File, Options)
2493    ;   '$do_load_file_2'(File, Absolute, Module, Action, Options)
2494    ).
2495
2496'$do_load_file_2'(File, Absolute, Module, Action, Options) :-
2497    '$source_file_property'(Absolute, number_of_clauses, OldClauses),
2498    statistics(cputime, OldTime),
2499
2500    '$setup_load'(ScopedFlags, OldSandBoxed, OldVerbose, OldAutoLevel, OldXRef,
2501                  Options),
2502
2503    '$compilation_level'(Level),
2504    '$load_msg_level'(load_file, Level, StartMsgLevel, DoneMsgLevel),
2505    '$print_message'(StartMsgLevel,
2506                     load_file(start(Level,
2507                                     file(File, Absolute)))),
2508
2509    (   memberchk(stream(FromStream), Options)
2510    ->  Input = stream
2511    ;   Input = source
2512    ),
2513
2514    (   Input == stream,
2515        (   '$option'(format(qlf), Options, source)
2516        ->  set_stream(FromStream, file_name(Absolute)),
2517            '$qload_stream'(FromStream, Module, Action, LM, Options)
2518        ;   '$consult_file'(stream(Absolute, FromStream, []),
2519                            Module, Action, LM, Options)
2520        )
2521    ->  true
2522    ;   Input == source,
2523        file_name_extension(_, Ext, Absolute),
2524        (   user:prolog_file_type(Ext, qlf),
2525            E = error(_,_),
2526            catch('$qload_file'(Absolute, Module, Action, LM, Options),
2527                  E,
2528                  print_message(warning, E))
2529        ->  true
2530        ;   '$consult_file'(Absolute, Module, Action, LM, Options)
2531        )
2532    ->  true
2533    ;   '$print_message'(error, load_file(failed(File))),
2534        fail
2535    ),
2536
2537    '$import_from_loaded_module'(LM, Module, Options),
2538
2539    '$source_file_property'(Absolute, number_of_clauses, NewClauses),
2540    statistics(cputime, Time),
2541    ClausesCreated is NewClauses - OldClauses,
2542    TimeUsed is Time - OldTime,
2543
2544    '$print_message'(DoneMsgLevel,
2545                     load_file(done(Level,
2546                                    file(File, Absolute),
2547                                    Action,
2548                                    LM,
2549                                    TimeUsed,
2550                                    ClausesCreated))),
2551
2552    '$restore_load'(ScopedFlags, OldSandBoxed, OldVerbose, OldAutoLevel, OldXRef).
2553
2554'$setup_load'(ScopedFlags, OldSandBoxed, OldVerbose, OldAutoLevel, OldXRef,
2555              Options) :-
2556    '$save_file_scoped_flags'(ScopedFlags),
2557    '$set_sandboxed_load'(Options, OldSandBoxed),
2558    '$set_verbose_load'(Options, OldVerbose),
2559    '$set_optimise_load'(Options),
2560    '$update_autoload_level'(Options, OldAutoLevel),
2561    '$set_no_xref'(OldXRef).
2562
2563'$restore_load'(ScopedFlags, OldSandBoxed, OldVerbose, OldAutoLevel, OldXRef) :-
2564    '$set_autoload_level'(OldAutoLevel),
2565    set_prolog_flag(xref, OldXRef),
2566    set_prolog_flag(verbose_load, OldVerbose),
2567    set_prolog_flag(sandboxed_load, OldSandBoxed),
2568    '$restore_file_scoped_flags'(ScopedFlags).
2569
2570
2571%!  '$save_file_scoped_flags'(-State) is det.
2572%!  '$restore_file_scoped_flags'(-State) is det.
2573%
2574%   Save/restore flags that are scoped to a compilation unit.
2575
2576'$save_file_scoped_flags'(State) :-
2577    current_predicate(findall/3),          % Not when doing boot compile
2578    !,
2579    findall(SavedFlag, '$save_file_scoped_flag'(SavedFlag), State).
2580'$save_file_scoped_flags'([]).
2581
2582'$save_file_scoped_flag'(Flag-Value) :-
2583    '$file_scoped_flag'(Flag, Default),
2584    (   current_prolog_flag(Flag, Value)
2585    ->  true
2586    ;   Value = Default
2587    ).
2588
2589'$file_scoped_flag'(generate_debug_info, true).
2590'$file_scoped_flag'(optimise,            false).
2591'$file_scoped_flag'(xref,                false).
2592
2593'$restore_file_scoped_flags'([]).
2594'$restore_file_scoped_flags'([Flag-Value|T]) :-
2595    set_prolog_flag(Flag, Value),
2596    '$restore_file_scoped_flags'(T).
2597
2598
2599%!  '$import_from_loaded_module'(LoadedModule, Module, Options) is det.
2600%
2601%   Import public predicates from LoadedModule into Module
2602
2603'$import_from_loaded_module'(LoadedModule, Module, Options) :-
2604    LoadedModule \== Module,
2605    atom(LoadedModule),
2606    !,
2607    '$option'(imports(Import), Options, all),
2608    '$option'(reexport(Reexport), Options, false),
2609    '$import_list'(Module, LoadedModule, Import, Reexport).
2610'$import_from_loaded_module'(_, _, _).
2611
2612
2613%!  '$set_verbose_load'(+Options, -Old) is det.
2614%
2615%   Set the =verbose_load= flag according to   Options and unify Old
2616%   with the old value.
2617
2618'$set_verbose_load'(Options, Old) :-
2619    current_prolog_flag(verbose_load, Old),
2620    (   memberchk(silent(Silent), Options)
2621    ->  (   '$negate'(Silent, Level0)
2622        ->  '$load_msg_compat'(Level0, Level)
2623        ;   Level = Silent
2624        ),
2625        set_prolog_flag(verbose_load, Level)
2626    ;   true
2627    ).
2628
2629'$negate'(true, false).
2630'$negate'(false, true).
2631
2632%!  '$set_sandboxed_load'(+Options, -Old) is det.
2633%
2634%   Update the Prolog flag  =sandboxed_load=   from  Options. Old is
2635%   unified with the old flag.
2636%
2637%   @error permission_error(leave, sandbox, -)
2638
2639'$set_sandboxed_load'(Options, Old) :-
2640    current_prolog_flag(sandboxed_load, Old),
2641    (   memberchk(sandboxed(SandBoxed), Options),
2642        '$enter_sandboxed'(Old, SandBoxed, New),
2643        New \== Old
2644    ->  set_prolog_flag(sandboxed_load, New)
2645    ;   true
2646    ).
2647
2648'$enter_sandboxed'(Old, New, SandBoxed) :-
2649    (   Old == false, New == true
2650    ->  SandBoxed = true,
2651        '$ensure_loaded_library_sandbox'
2652    ;   Old == true, New == false
2653    ->  throw(error(permission_error(leave, sandbox, -), _))
2654    ;   SandBoxed = Old
2655    ).
2656'$enter_sandboxed'(false, true, true).
2657
2658'$ensure_loaded_library_sandbox' :-
2659    source_file_property(library(sandbox), module(sandbox)),
2660    !.
2661'$ensure_loaded_library_sandbox' :-
2662    load_files(library(sandbox), [if(not_loaded), silent(true)]).
2663
2664'$set_optimise_load'(Options) :-
2665    (   '$option'(optimise(Optimise), Options)
2666    ->  set_prolog_flag(optimise, Optimise)
2667    ;   true
2668    ).
2669
2670'$set_no_xref'(OldXRef) :-
2671    (   current_prolog_flag(xref, OldXRef)
2672    ->  true
2673    ;   OldXRef = false
2674    ),
2675    set_prolog_flag(xref, false).
2676
2677
2678%!  '$update_autoload_level'(+Options, -OldLevel)
2679%
2680%   Update the '$autoload_nesting' and return the old value.
2681
2682:- thread_local
2683    '$autoload_nesting'/1.
2684
2685'$update_autoload_level'(Options, AutoLevel) :-
2686    '$option'(autoload(Autoload), Options, false),
2687    (   '$autoload_nesting'(CurrentLevel)
2688    ->  AutoLevel = CurrentLevel
2689    ;   AutoLevel = 0
2690    ),
2691    (   Autoload == false
2692    ->  true
2693    ;   NewLevel is AutoLevel + 1,
2694        '$set_autoload_level'(NewLevel)
2695    ).
2696
2697'$set_autoload_level'(New) :-
2698    retractall('$autoload_nesting'(_)),
2699    asserta('$autoload_nesting'(New)).
2700
2701
2702%!  '$print_message'(+Level, +Term) is det.
2703%
2704%   As print_message/2, but deal with  the   fact  that  the message
2705%   system might not yet be loaded.
2706
2707'$print_message'(Level, Term) :-
2708    current_predicate(system:print_message/2),
2709    !,
2710    print_message(Level, Term).
2711'$print_message'(warning, Term) :-
2712    source_location(File, Line),
2713    !,
2714    format(user_error, 'WARNING: ~w:~w: ~p~n', [File, Line, Term]).
2715'$print_message'(error, Term) :-
2716    !,
2717    source_location(File, Line),
2718    !,
2719    format(user_error, 'ERROR: ~w:~w: ~p~n', [File, Line, Term]).
2720'$print_message'(_Level, _Term).
2721
2722'$print_message_fail'(E) :-
2723    '$print_message'(error, E),
2724    fail.
2725
2726%!  '$consult_file'(+Path, +Module, -Action, -LoadedIn, +Options)
2727%
2728%   Called  from  '$do_load_file'/4  using  the   goal  returned  by
2729%   '$consult_goal'/2. This means that the  calling conventions must
2730%   be kept synchronous with '$qload_file'/6.
2731
2732'$consult_file'(Absolute, Module, What, LM, Options) :-
2733    '$current_source_module'(Module),   % same module
2734    !,
2735    '$consult_file_2'(Absolute, Module, What, LM, Options).
2736'$consult_file'(Absolute, Module, What, LM, Options) :-
2737    '$set_source_module'(OldModule, Module),
2738    '$ifcompiling'('$qlf_start_sub_module'(Module)),
2739    '$consult_file_2'(Absolute, Module, What, LM, Options),
2740    '$ifcompiling'('$qlf_end_part'),
2741    '$set_source_module'(OldModule).
2742
2743'$consult_file_2'(Absolute, Module, What, LM, Options) :-
2744    '$set_source_module'(OldModule, Module),
2745    '$load_id'(Absolute, Id, Modified, Options),
2746    '$compile_type'(What),
2747    '$save_lex_state'(LexState, Options),
2748    '$set_dialect'(Options),
2749    setup_call_cleanup(
2750        '$start_consult'(Id, Modified),
2751        '$load_file'(Absolute, Id, LM, Options),
2752        '$end_consult'(Id, LexState, OldModule)).
2753
2754'$end_consult'(Id, LexState, OldModule) :-
2755    '$end_consult'(Id),
2756    '$restore_lex_state'(LexState),
2757    '$set_source_module'(OldModule).
2758
2759
2760:- create_prolog_flag(emulated_dialect, swi, [type(atom)]).
2761
2762%!  '$save_lex_state'(-LexState, +Options) is det.
2763
2764'$save_lex_state'(State, Options) :-
2765    memberchk(scope_settings(false), Options),
2766    !,
2767    State = (-).
2768'$save_lex_state'(lexstate(Style, Dialect), _) :-
2769    '$style_check'(Style, Style),
2770    current_prolog_flag(emulated_dialect, Dialect).
2771
2772'$restore_lex_state'(-) :- !.
2773'$restore_lex_state'(lexstate(Style, Dialect)) :-
2774    '$style_check'(_, Style),
2775    set_prolog_flag(emulated_dialect, Dialect).
2776
2777'$set_dialect'(Options) :-
2778    memberchk(dialect(Dialect), Options),
2779    !,
2780    '$expects_dialect'(Dialect).
2781'$set_dialect'(_).
2782
2783'$load_id'(stream(Id, _, _), Id, Modified, Options) :-
2784    !,
2785    '$modified_id'(Id, Modified, Options).
2786'$load_id'(Id, Id, Modified, Options) :-
2787    '$modified_id'(Id, Modified, Options).
2788
2789'$modified_id'(_, Modified, Options) :-
2790    '$option'(modified(Stamp), Options, Def),
2791    Stamp \== Def,
2792    !,
2793    Modified = Stamp.
2794'$modified_id'(Id, Modified, _) :-
2795    catch(time_file(Id, Modified),
2796          error(_, _),
2797          fail),
2798    !.
2799'$modified_id'(_, 0.0, _).
2800
2801
2802'$compile_type'(What) :-
2803    '$compilation_mode'(How),
2804    (   How == database
2805    ->  What = compiled
2806    ;   How == qlf
2807    ->  What = '*qcompiled*'
2808    ;   What = 'boot compiled'
2809    ).
2810
2811%!  '$assert_load_context_module'(+File, -Module, -Options)
2812%
2813%   Record the module a file was loaded from (see make/0). The first
2814%   clause deals with loading from  another   file.  On reload, this
2815%   clause will be discarded by  $start_consult/1. The second clause
2816%   deals with reload from the toplevel.   Here  we avoid creating a
2817%   duplicate dynamic (i.e., not related to a source) clause.
2818
2819:- dynamic
2820    '$load_context_module'/3.
2821:- multifile
2822    '$load_context_module'/3.
2823
2824'$assert_load_context_module'(_, _, Options) :-
2825    memberchk(register(false), Options),
2826    !.
2827'$assert_load_context_module'(File, Module, Options) :-
2828    source_location(FromFile, Line),
2829    !,
2830    '$master_file'(FromFile, MasterFile),
2831    '$check_load_non_module'(File, Module),
2832    '$add_dialect'(Options, Options1),
2833    '$load_ctx_options'(Options1, Options2),
2834    '$store_admin_clause'(
2835        system:'$load_context_module'(File, Module, Options2),
2836        _Layout, MasterFile, FromFile:Line).
2837'$assert_load_context_module'(File, Module, Options) :-
2838    '$check_load_non_module'(File, Module),
2839    '$add_dialect'(Options, Options1),
2840    '$load_ctx_options'(Options1, Options2),
2841    (   clause('$load_context_module'(File, Module, _), true, Ref),
2842        \+ clause_property(Ref, file(_)),
2843        erase(Ref)
2844    ->  true
2845    ;   true
2846    ),
2847    assertz('$load_context_module'(File, Module, Options2)).
2848
2849'$add_dialect'(Options0, Options) :-
2850    current_prolog_flag(emulated_dialect, Dialect), Dialect \== swi,
2851    !,
2852    Options = [dialect(Dialect)|Options0].
2853'$add_dialect'(Options, Options).
2854
2855%!  '$load_ctx_options'(+Options, -CtxOptions) is det.
2856%
2857%   Select the load options that  determine   the  load semantics to
2858%   perform a proper reload. Delete the others.
2859
2860'$load_ctx_options'(Options, CtxOptions) :-
2861    '$load_ctx_options2'(Options, CtxOptions0),
2862    sort(CtxOptions0, CtxOptions).
2863
2864'$load_ctx_options2'([], []).
2865'$load_ctx_options2'([H|T0], [H|T]) :-
2866    '$load_ctx_option'(H),
2867    !,
2868    '$load_ctx_options2'(T0, T).
2869'$load_ctx_options2'([_|T0], T) :-
2870    '$load_ctx_options2'(T0, T).
2871
2872'$load_ctx_option'(derived_from(_)).
2873'$load_ctx_option'(dialect(_)).
2874'$load_ctx_option'(encoding(_)).
2875'$load_ctx_option'(imports(_)).
2876'$load_ctx_option'(reexport(_)).
2877
2878
2879%!  '$check_load_non_module'(+File) is det.
2880%
2881%   Test  that  a  non-module  file  is  not  loaded  into  multiple
2882%   contexts.
2883
2884'$check_load_non_module'(File, _) :-
2885    '$current_module'(_, File),
2886    !.          % File is a module file
2887'$check_load_non_module'(File, Module) :-
2888    '$load_context_module'(File, OldModule, _),
2889    Module \== OldModule,
2890    !,
2891    format(atom(Msg),
2892           'Non-module file already loaded into module ~w; \c
2893               trying to load into ~w',
2894           [OldModule, Module]),
2895    throw(error(permission_error(load, source, File),
2896                context(load_files/2, Msg))).
2897'$check_load_non_module'(_, _).
2898
2899%!  '$load_file'(+Path, +Id, -Module, +Options)
2900%
2901%   '$load_file'/4 does the actual loading.
2902%
2903%   state(FirstTerm:boolean,
2904%         Module:atom,
2905%         AtEnd:atom,
2906%         Stop:boolean,
2907%         Id:atom,
2908%         Dialect:atom)
2909
2910'$load_file'(Path, Id, Module, Options) :-
2911    State = state(true, _, true, false, Id, -),
2912    (   '$source_term'(Path, _Read, _Layout, Term, Layout,
2913                       _Stream, Options),
2914        '$valid_term'(Term),
2915        (   arg(1, State, true)
2916        ->  '$first_term'(Term, Layout, Id, State, Options),
2917            nb_setarg(1, State, false)
2918        ;   '$compile_term'(Term, Layout, Id)
2919        ),
2920        arg(4, State, true)
2921    ;   '$fixup_reconsult'(Id),
2922        '$end_load_file'(State)
2923    ),
2924    !,
2925    arg(2, State, Module).
2926
2927'$valid_term'(Var) :-
2928    var(Var),
2929    !,
2930    print_message(error, error(instantiation_error, _)).
2931'$valid_term'(Term) :-
2932    Term \== [].
2933
2934'$end_load_file'(State) :-
2935    arg(1, State, true),           % empty file
2936    !,
2937    nb_setarg(2, State, Module),
2938    arg(5, State, Id),
2939    '$current_source_module'(Module),
2940    '$ifcompiling'('$qlf_start_file'(Id)),
2941    '$ifcompiling'('$qlf_end_part').
2942'$end_load_file'(State) :-
2943    arg(3, State, End),
2944    '$end_load_file'(End, State).
2945
2946'$end_load_file'(true, _).
2947'$end_load_file'(end_module, State) :-
2948    arg(2, State, Module),
2949    '$check_export'(Module),
2950    '$ifcompiling'('$qlf_end_part').
2951'$end_load_file'(end_non_module, _State) :-
2952    '$ifcompiling'('$qlf_end_part').
2953
2954
2955'$first_term'(?-(Directive), Layout, Id, State, Options) :-
2956    !,
2957    '$first_term'(:-(Directive), Layout, Id, State, Options).
2958'$first_term'(:-(Directive), _Layout, Id, State, Options) :-
2959    nonvar(Directive),
2960    (   (   Directive = module(Name, Public)
2961        ->  Imports = []
2962        ;   Directive = module(Name, Public, Imports)
2963        )
2964    ->  !,
2965        '$module_name'(Name, Id, Module, Options),
2966        '$start_module'(Module, Public, State, Options),
2967        '$module3'(Imports)
2968    ;   Directive = expects_dialect(Dialect)
2969    ->  !,
2970        '$set_dialect'(Dialect, State),
2971        fail                        % Still consider next term as first
2972    ).
2973'$first_term'(Term, Layout, Id, State, Options) :-
2974    '$start_non_module'(Id, State, Options),
2975    '$compile_term'(Term, Layout, Id).
2976
2977'$compile_term'(Term, Layout, Id) :-
2978    '$compile_term'(Term, Layout, Id, -).
2979
2980'$compile_term'(Var, _Layout, _Id, _Src) :-
2981    var(Var),
2982    !,
2983    '$instantiation_error'(Var).
2984'$compile_term'((?-Directive), _Layout, Id, _) :-
2985    !,
2986    '$execute_directive'(Directive, Id).
2987'$compile_term'((:-Directive), _Layout, Id, _) :-
2988    !,
2989    '$execute_directive'(Directive, Id).
2990'$compile_term'('$source_location'(File, Line):Term, Layout, Id, _) :-
2991    !,
2992    '$compile_term'(Term, Layout, Id, File:Line).
2993'$compile_term'(Clause, Layout, Id, SrcLoc) :-
2994    E = error(_,_),
2995    catch('$store_clause'(Clause, Layout, Id, SrcLoc), E,
2996          '$print_message'(error, E)).
2997
2998'$start_non_module'(Id, _State, Options) :-
2999    '$option'(must_be_module(true), Options, false),
3000    !,
3001    throw(error(domain_error(module_file, Id), _)).
3002'$start_non_module'(Id, State, _Options) :-
3003    '$current_source_module'(Module),
3004    '$ifcompiling'('$qlf_start_file'(Id)),
3005    '$qset_dialect'(State),
3006    nb_setarg(2, State, Module),
3007    nb_setarg(3, State, end_non_module).
3008
3009%!  '$set_dialect'(+Dialect, +State)
3010%
3011%   Sets the expected dialect. This is difficult if we are compiling
3012%   a .qlf file using qcompile/1 because   the file is already open,
3013%   while we are looking for the first term to decide wether this is
3014%   a module or not. We save the   dialect  and set it after opening
3015%   the file or module.
3016%
3017%   Note that expects_dialect/1 itself may   be  autoloaded from the
3018%   library.
3019
3020'$set_dialect'(Dialect, State) :-
3021    '$compilation_mode'(qlf, database),
3022    !,
3023    '$expects_dialect'(Dialect),
3024    '$compilation_mode'(_, qlf),
3025    nb_setarg(6, State, Dialect).
3026'$set_dialect'(Dialect, _) :-
3027    '$expects_dialect'(Dialect).
3028
3029'$qset_dialect'(State) :-
3030    '$compilation_mode'(qlf),
3031    arg(6, State, Dialect), Dialect \== (-),
3032    !,
3033    '$add_directive_wic'('$expects_dialect'(Dialect)).
3034'$qset_dialect'(_).
3035
3036'$expects_dialect'(Dialect) :-
3037    Dialect == swi,
3038    !,
3039    set_prolog_flag(emulated_dialect, Dialect).
3040'$expects_dialect'(Dialect) :-
3041    current_predicate(expects_dialect/1),
3042    !,
3043    expects_dialect(Dialect).
3044'$expects_dialect'(Dialect) :-
3045    use_module(library(dialect), [expects_dialect/1]),
3046    expects_dialect(Dialect).
3047
3048
3049                 /*******************************
3050                 *           MODULES            *
3051                 *******************************/
3052
3053'$start_module'(Module, _Public, State, _Options) :-
3054    '$current_module'(Module, OldFile),
3055    source_location(File, _Line),
3056    OldFile \== File, OldFile \== [],
3057    same_file(OldFile, File),
3058    !,
3059    nb_setarg(2, State, Module),
3060    nb_setarg(4, State, true).      % Stop processing
3061'$start_module'(Module, Public, State, Options) :-
3062    arg(5, State, File),
3063    nb_setarg(2, State, Module),
3064    source_location(_File, Line),
3065    '$option'(redefine_module(Action), Options, false),
3066    '$module_class'(File, Class, Super),
3067    '$redefine_module'(Module, File, Action),
3068    '$declare_module'(Module, Class, Super, File, Line, false),
3069    '$export_list'(Public, Module, Ops),
3070    '$ifcompiling'('$qlf_start_module'(Module)),
3071    '$export_ops'(Ops, Module, File),
3072    '$qset_dialect'(State),
3073    nb_setarg(3, State, end_module).
3074
3075
3076%!  '$module3'(+Spec) is det.
3077%
3078%   Handle the 3th argument of a module declartion.
3079
3080'$module3'(Var) :-
3081    var(Var),
3082    !,
3083    '$instantiation_error'(Var).
3084'$module3'([]) :- !.
3085'$module3'([H|T]) :-
3086    !,
3087    '$module3'(H),
3088    '$module3'(T).
3089'$module3'(Id) :-
3090    use_module(library(dialect/Id)).
3091
3092%!  '$module_name'(?Name, +Id, -Module, +Options) is semidet.
3093%
3094%   Determine the module name.  There are some cases:
3095%
3096%     - Option module(Module) is given.  In that case, use this
3097%       module and if Module is the load context, ignore the module
3098%       header.
3099%     - The initial name is unbound.  Use the base name of the
3100%       source identifier (normally the file name).  Compatibility
3101%       to Ciao.  This might change; I think it is wiser to use
3102%       the full unique source identifier.
3103
3104'$module_name'(_, _, Module, Options) :-
3105    '$option'(module(Module), Options),
3106    !,
3107    '$current_source_module'(Context),
3108    Context \== Module.                     % cause '$first_term'/5 to fail.
3109'$module_name'(Var, Id, Module, Options) :-
3110    var(Var),
3111    !,
3112    file_base_name(Id, File),
3113    file_name_extension(Var, _, File),
3114    '$module_name'(Var, Id, Module, Options).
3115'$module_name'(Reserved, _, _, _) :-
3116    '$reserved_module'(Reserved),
3117    !,
3118    throw(error(permission_error(load, module, Reserved), _)).
3119'$module_name'(Module, _Id, Module, _).
3120
3121
3122'$reserved_module'(system).
3123'$reserved_module'(user).
3124
3125
3126%!  '$redefine_module'(+Module, +File, -Redefine)
3127
3128'$redefine_module'(_Module, _, false) :- !.
3129'$redefine_module'(Module, File, true) :-
3130    !,
3131    (   module_property(Module, file(OldFile)),
3132        File \== OldFile
3133    ->  unload_file(OldFile)
3134    ;   true
3135    ).
3136'$redefine_module'(Module, File, ask) :-
3137    (   stream_property(user_input, tty(true)),
3138        module_property(Module, file(OldFile)),
3139        File \== OldFile,
3140        '$rdef_response'(Module, OldFile, File, true)
3141    ->  '$redefine_module'(Module, File, true)
3142    ;   true
3143    ).
3144
3145'$rdef_response'(Module, OldFile, File, Ok) :-
3146    repeat,
3147    print_message(query, redefine_module(Module, OldFile, File)),
3148    get_single_char(Char),
3149    '$rdef_response'(Char, Ok0),
3150    !,
3151    Ok = Ok0.
3152
3153'$rdef_response'(Char, true) :-
3154    memberchk(Char, `yY`),
3155    format(user_error, 'yes~n', []).
3156'$rdef_response'(Char, false) :-
3157    memberchk(Char, `nN`),
3158    format(user_error, 'no~n', []).
3159'$rdef_response'(Char, _) :-
3160    memberchk(Char, `a`),
3161    format(user_error, 'abort~n', []),
3162    abort.
3163'$rdef_response'(_, _) :-
3164    print_message(help, redefine_module_reply),
3165    fail.
3166
3167
3168%!  '$module_class'(+File, -Class, -Super) is det.
3169%
3170%   Determine  the  file  class  and  initial  module  from  which  File
3171%   inherits. All boot and library modules  as   well  as  the -F script
3172%   files inherit from `system`, while all   normal user modules inherit
3173%   from `user`.
3174
3175'$module_class'(File, Class, system) :-
3176    current_prolog_flag(home, Home),
3177    sub_atom(File, 0, Len, _, Home),
3178    (   sub_atom(File, Len, _, _, '/boot/')
3179    ->  Class = system
3180    ;   '$lib_prefix'(Prefix),
3181        sub_atom(File, Len, _, _, Prefix)
3182    ->  Class = library
3183    ;   file_directory_name(File, Home),
3184        file_name_extension(_, rc, File)
3185    ->  Class = library
3186    ),
3187    !.
3188'$module_class'(_, user, user).
3189
3190'$lib_prefix'('/library').
3191'$lib_prefix'('/xpce/prolog/').
3192
3193'$check_export'(Module) :-
3194    '$undefined_export'(Module, UndefList),
3195    (   '$member'(Undef, UndefList),
3196        strip_module(Undef, _, Local),
3197        print_message(error,
3198                      undefined_export(Module, Local)),
3199        fail
3200    ;   true
3201    ).
3202
3203
3204%!  '$import_list'(+TargetModule, +FromModule, +Import, +Reexport) is det.
3205%
3206%   Import from FromModule to TargetModule. Import  is one of =all=,
3207%   a list of optionally  mapped  predicate   indicators  or  a term
3208%   except(Import).
3209
3210'$import_list'(_, _, Var, _) :-
3211    var(Var),
3212    !,
3213    throw(error(instantitation_error, _)).
3214'$import_list'(Target, Source, all, Reexport) :-
3215    !,
3216    '$exported_ops'(Source, Import, Predicates),
3217    '$module_property'(Source, exports(Predicates)),
3218    '$import_all'(Import, Target, Source, Reexport, weak).
3219'$import_list'(Target, Source, except(Spec), Reexport) :-
3220    !,
3221    '$exported_ops'(Source, Export, Predicates),
3222    '$module_property'(Source, exports(Predicates)),
3223    (   is_list(Spec)
3224    ->  true
3225    ;   throw(error(type_error(list, Spec), _))
3226    ),
3227    '$import_except'(Spec, Export, Import),
3228    '$import_all'(Import, Target, Source, Reexport, weak).
3229'$import_list'(Target, Source, Import, Reexport) :-
3230    !,
3231    is_list(Import),
3232    !,
3233    '$import_all'(Import, Target, Source, Reexport, strong).
3234'$import_list'(_, _, Import, _) :-
3235    throw(error(type_error(import_specifier, Import))).
3236
3237
3238'$import_except'([], List, List).
3239'$import_except'([H|T], List0, List) :-
3240    '$import_except_1'(H, List0, List1),
3241    '$import_except'(T, List1, List).
3242
3243'$import_except_1'(Var, _, _) :-
3244    var(Var),
3245    !,
3246    throw(error(instantitation_error, _)).
3247'$import_except_1'(PI as N, List0, List) :-
3248    '$pi'(PI), atom(N),
3249    !,
3250    '$canonical_pi'(PI, CPI),
3251    '$import_as'(CPI, N, List0, List).
3252'$import_except_1'(op(P,A,N), List0, List) :-
3253    !,
3254    '$remove_ops'(List0, op(P,A,N), List).
3255'$import_except_1'(PI, List0, List) :-
3256    '$pi'(PI),
3257    !,
3258    '$canonical_pi'(PI, CPI),
3259    '$select'(P, List0, List),
3260    '$canonical_pi'(CPI, P),
3261    !.
3262'$import_except_1'(Except, _, _) :-
3263    throw(error(type_error(import_specifier, Except), _)).
3264
3265'$import_as'(CPI, N, [PI2|T], [CPI as N|T]) :-
3266    '$canonical_pi'(PI2, CPI),
3267    !.
3268'$import_as'(PI, N, [H|T0], [H|T]) :-
3269    !,
3270    '$import_as'(PI, N, T0, T).
3271'$import_as'(PI, _, _, _) :-
3272    throw(error(existence_error(export, PI), _)).
3273
3274'$pi'(N/A) :- atom(N), integer(A), !.
3275'$pi'(N//A) :- atom(N), integer(A).
3276
3277'$canonical_pi'(N//A0, N/A) :-
3278    A is A0 + 2.
3279'$canonical_pi'(PI, PI).
3280
3281'$remove_ops'([], _, []).
3282'$remove_ops'([Op|T0], Pattern, T) :-
3283    subsumes_term(Pattern, Op),
3284    !,
3285    '$remove_ops'(T0, Pattern, T).
3286'$remove_ops'([H|T0], Pattern, [H|T]) :-
3287    '$remove_ops'(T0, Pattern, T).
3288
3289
3290%!  '$import_all'(+Import, +Context, +Source, +Reexport, +Strength)
3291
3292'$import_all'(Import, Context, Source, Reexport, Strength) :-
3293    '$import_all2'(Import, Context, Source, Imported, ImpOps, Strength),
3294    (   Reexport == true,
3295        (   '$list_to_conj'(Imported, Conj)
3296        ->  export(Context:Conj),
3297            '$ifcompiling'('$add_directive_wic'(export(Context:Conj)))
3298        ;   true
3299        ),
3300        source_location(File, _Line),
3301        '$export_ops'(ImpOps, Context, File)
3302    ;   true
3303    ).
3304
3305%!  '$import_all2'(+Imports, +Context, +Source, -Imported, -ImpOps, +Strength)
3306
3307'$import_all2'([], _, _, [], [], _).
3308'$import_all2'([PI as NewName|Rest], Context, Source,
3309               [NewName/Arity|Imported], ImpOps, Strength) :-
3310    !,
3311    '$canonical_pi'(PI, Name/Arity),
3312    length(Args, Arity),
3313    Head =.. [Name|Args],
3314    NewHead =.. [NewName|Args],
3315    (   '$get_predicate_attribute'(Source:Head, transparent, 1)
3316    ->  '$set_predicate_attribute'(Context:NewHead, transparent, true)
3317    ;   true
3318    ),
3319    (   source_location(File, Line)
3320    ->  E = error(_,_),
3321        catch('$store_admin_clause'((NewHead :- Source:Head),
3322                                    _Layout, File, File:Line),
3323              E, '$print_message'(error, E))
3324    ;   assertz((NewHead :- !, Source:Head)) % ! avoids problems with
3325    ),                                       % duplicate load
3326    '$import_all2'(Rest, Context, Source, Imported, ImpOps, Strength).
3327'$import_all2'([op(P,A,N)|Rest], Context, Source, Imported,
3328               [op(P,A,N)|ImpOps], Strength) :-
3329    !,
3330    '$import_ops'(Context, Source, op(P,A,N)),
3331    '$import_all2'(Rest, Context, Source, Imported, ImpOps, Strength).
3332'$import_all2'([Pred|Rest], Context, Source, [Pred|Imported], ImpOps, Strength) :-
3333    Error = error(_,_),
3334    catch(Context:'$import'(Source:Pred, Strength), Error,
3335          print_message(error, Error)),
3336    '$ifcompiling'('$import_wic'(Source, Pred, Strength)),
3337    '$import_all2'(Rest, Context, Source, Imported, ImpOps, Strength).
3338
3339
3340'$list_to_conj'([One], One) :- !.
3341'$list_to_conj'([H|T], (H,Rest)) :-
3342    '$list_to_conj'(T, Rest).
3343
3344%!  '$exported_ops'(+Module, -Ops, ?Tail) is det.
3345%
3346%   Ops is a list of op(P,A,N) terms representing the operators
3347%   exported from Module.
3348
3349'$exported_ops'(Module, Ops, Tail) :-
3350    '$c_current_predicate'(_, Module:'$exported_op'(_,_,_)),
3351    !,
3352    findall(op(P,A,N), Module:'$exported_op'(P,A,N), Ops, Tail).
3353'$exported_ops'(_, Ops, Ops).
3354
3355'$exported_op'(Module, P, A, N) :-
3356    '$c_current_predicate'(_, Module:'$exported_op'(_,_,_)),
3357    Module:'$exported_op'(P, A, N).
3358
3359%!  '$import_ops'(+Target, +Source, +Pattern)
3360%
3361%   Import the operators export from Source into the module table of
3362%   Target.  We only import operators that unify with Pattern.
3363
3364'$import_ops'(To, From, Pattern) :-
3365    ground(Pattern),
3366    !,
3367    Pattern = op(P,A,N),
3368    op(P,A,To:N),
3369    (   '$exported_op'(From, P, A, N)
3370    ->  true
3371    ;   print_message(warning, no_exported_op(From, Pattern))
3372    ).
3373'$import_ops'(To, From, Pattern) :-
3374    (   '$exported_op'(From, Pri, Assoc, Name),
3375        Pattern = op(Pri, Assoc, Name),
3376        op(Pri, Assoc, To:Name),
3377        fail
3378    ;   true
3379    ).
3380
3381
3382%!  '$export_list'(+Declarations, +Module, -Ops)
3383%
3384%   Handle the export list of the module declaration for Module
3385%   associated to File.
3386
3387'$export_list'(Decls, Module, Ops) :-
3388    is_list(Decls),
3389    !,
3390    '$do_export_list'(Decls, Module, Ops).
3391'$export_list'(Decls, _, _) :-
3392    var(Decls),
3393    throw(error(instantiation_error, _)).
3394'$export_list'(Decls, _, _) :-
3395    throw(error(type_error(list, Decls), _)).
3396
3397'$do_export_list'([], _, []) :- !.
3398'$do_export_list'([H|T], Module, Ops) :-
3399    !,
3400    E = error(_,_),
3401    catch('$export1'(H, Module, Ops, Ops1),
3402          E, ('$print_message'(error, E), Ops = Ops1)),
3403    '$do_export_list'(T, Module, Ops1).
3404
3405'$export1'(Var, _, _, _) :-
3406    var(Var),
3407    !,
3408    throw(error(instantiation_error, _)).
3409'$export1'(Op, _, [Op|T], T) :-
3410    Op = op(_,_,_),
3411    !.
3412'$export1'(PI0, Module, Ops, Ops) :-
3413    strip_module(Module:PI0, M, PI),
3414    (   PI = (_//_)
3415    ->  non_terminal(M:PI)
3416    ;   true
3417    ),
3418    export(M:PI).
3419
3420'$export_ops'([op(Pri, Assoc, Name)|T], Module, File) :-
3421    E = error(_,_),
3422    catch(( '$execute_directive'(op(Pri, Assoc, Module:Name), File),
3423            '$export_op'(Pri, Assoc, Name, Module, File)
3424          ),
3425          E, '$print_message'(error, E)),
3426    '$export_ops'(T, Module, File).
3427'$export_ops'([], _, _).
3428
3429'$export_op'(Pri, Assoc, Name, Module, File) :-
3430    (   '$get_predicate_attribute'(Module:'$exported_op'(_,_,_), defined, 1)
3431    ->  true
3432    ;   '$execute_directive'(discontiguous(Module:'$exported_op'/3), File)
3433    ),
3434    '$store_admin_clause'('$exported_op'(Pri, Assoc, Name), _Layout, File, -).
3435
3436%!  '$execute_directive'(:Goal, +File) is det.
3437%
3438%   Execute the argument of :- or ?- while loading a file.
3439
3440'$execute_directive'(Goal, F) :-
3441    '$execute_directive_2'(Goal, F).
3442
3443'$execute_directive_2'(encoding(Encoding), _F) :-
3444    !,
3445    (   '$load_input'(_F, S)
3446    ->  set_stream(S, encoding(Encoding))
3447    ).
3448'$execute_directive_2'(Goal, _) :-
3449    \+ '$compilation_mode'(database),
3450    !,
3451    '$add_directive_wic2'(Goal, Type),
3452    (   Type == call                % suspend compiling into .qlf file
3453    ->  '$compilation_mode'(Old, database),
3454        setup_call_cleanup(
3455            '$directive_mode'(OldDir, Old),
3456            '$execute_directive_3'(Goal),
3457            ( '$set_compilation_mode'(Old),
3458              '$set_directive_mode'(OldDir)
3459            ))
3460    ;   '$execute_directive_3'(Goal)
3461    ).
3462'$execute_directive_2'(Goal, _) :-
3463    '$execute_directive_3'(Goal).
3464
3465'$execute_directive_3'(Goal) :-
3466    '$current_source_module'(Module),
3467    '$valid_directive'(Module:Goal),
3468    !,
3469    (   '$pattr_directive'(Goal, Module)
3470    ->  true
3471    ;   Term = error(_,_),
3472        catch(Module:Goal, Term, '$exception_in_directive'(Term))
3473    ->  true
3474    ;   '$print_message'(warning, goal_failed(directive, Module:Goal)),
3475        fail
3476    ).
3477'$execute_directive_3'(_).
3478
3479
3480%!  '$valid_directive'(:Directive) is det.
3481%
3482%   If   the   flag   =sandboxed_load=   is   =true=,   this   calls
3483%   prolog:sandbox_allowed_directive/1. This call can deny execution
3484%   of the directive by throwing an exception.
3485
3486:- multifile prolog:sandbox_allowed_directive/1.
3487:- multifile prolog:sandbox_allowed_clause/1.
3488:- meta_predicate '$valid_directive'(:).
3489
3490'$valid_directive'(_) :-
3491    current_prolog_flag(sandboxed_load, false),
3492    !.
3493'$valid_directive'(Goal) :-
3494    Error = error(Formal, _),
3495    catch(prolog:sandbox_allowed_directive(Goal), Error, true),
3496    !,
3497    (   var(Formal)
3498    ->  true
3499    ;   print_message(error, Error),
3500        fail
3501    ).
3502'$valid_directive'(Goal) :-
3503    print_message(error,
3504                  error(permission_error(execute,
3505                                         sandboxed_directive,
3506                                         Goal), _)),
3507    fail.
3508
3509'$exception_in_directive'(Term) :-
3510    '$print_message'(error, Term),
3511    fail.
3512
3513%       Note that the list, consult and ensure_loaded directives are already
3514%       handled at compile time and therefore should not go into the
3515%       intermediate code file.
3516
3517'$add_directive_wic2'(Goal, Type) :-
3518    '$common_goal_type'(Goal, Type),
3519    !,
3520    (   Type == load
3521    ->  true
3522    ;   '$current_source_module'(Module),
3523        '$add_directive_wic'(Module:Goal)
3524    ).
3525'$add_directive_wic2'(Goal, _) :-
3526    (   '$compilation_mode'(qlf)    % no problem for qlf files
3527    ->  true
3528    ;   print_message(error, mixed_directive(Goal))
3529    ).
3530
3531'$common_goal_type'((A,B), Type) :-
3532    !,
3533    '$common_goal_type'(A, Type),
3534    '$common_goal_type'(B, Type).
3535'$common_goal_type'((A;B), Type) :-
3536    !,
3537    '$common_goal_type'(A, Type),
3538    '$common_goal_type'(B, Type).
3539'$common_goal_type'((A->B), Type) :-
3540    !,
3541    '$common_goal_type'(A, Type),
3542    '$common_goal_type'(B, Type).
3543'$common_goal_type'(Goal, Type) :-
3544    '$goal_type'(Goal, Type).
3545
3546'$goal_type'(Goal, Type) :-
3547    (   '$load_goal'(Goal)
3548    ->  Type = load
3549    ;   Type = call
3550    ).
3551
3552'$load_goal'([_|_]).
3553'$load_goal'(consult(_)).
3554'$load_goal'(load_files(_)).
3555'$load_goal'(load_files(_,Options)) :-
3556    memberchk(qcompile(QlfMode), Options),
3557    '$qlf_part_mode'(QlfMode).
3558'$load_goal'(ensure_loaded(_)) :- '$compilation_mode'(wic).
3559'$load_goal'(use_module(_))    :- '$compilation_mode'(wic).
3560'$load_goal'(use_module(_, _)) :- '$compilation_mode'(wic).
3561
3562'$qlf_part_mode'(part).
3563'$qlf_part_mode'(true).                 % compatibility
3564
3565
3566                /********************************
3567                *        COMPILE A CLAUSE       *
3568                *********************************/
3569
3570%!  '$store_admin_clause'(+Clause, ?Layout, +Owner, +SrcLoc) is det.
3571%
3572%   Store a clause into the   database  for administrative purposes.
3573%   This bypasses sanity checking.
3574
3575'$store_admin_clause'(Clause, Layout, Owner, SrcLoc) :-
3576    Owner \== (-),
3577    !,
3578    setup_call_cleanup(
3579        '$start_aux'(Owner, Context),
3580        '$store_admin_clause2'(Clause, Layout, Owner, SrcLoc),
3581        '$end_aux'(Owner, Context)).
3582'$store_admin_clause'(Clause, Layout, File, SrcLoc) :-
3583    '$store_admin_clause2'(Clause, Layout, File, SrcLoc).
3584
3585'$store_admin_clause2'(Clause, _Layout, File, SrcLoc) :-
3586    (   '$compilation_mode'(database)
3587    ->  '$record_clause'(Clause, File, SrcLoc)
3588    ;   '$record_clause'(Clause, File, SrcLoc, Ref),
3589        '$qlf_assert_clause'(Ref, development)
3590    ).
3591
3592%!  '$store_clause'(+Clause, ?Layout, +Owner, +SrcLoc) is det.
3593%
3594%   Store a clause into the database.
3595%
3596%   @arg    Owner is the file-id that owns the clause
3597%   @arg    SrcLoc is the file:line term where the clause
3598%           originates from.
3599
3600'$store_clause'((_, _), _, _, _) :-
3601    !,
3602    print_message(error, cannot_redefine_comma),
3603    fail.
3604'$store_clause'(Clause, _Layout, File, SrcLoc) :-
3605    '$valid_clause'(Clause),
3606    !,
3607    (   '$compilation_mode'(database)
3608    ->  '$record_clause'(Clause, File, SrcLoc)
3609    ;   '$record_clause'(Clause, File, SrcLoc, Ref),
3610        '$qlf_assert_clause'(Ref, development)
3611    ).
3612
3613'$valid_clause'(_) :-
3614    current_prolog_flag(sandboxed_load, false),
3615    !.
3616'$valid_clause'(Clause) :-
3617    \+ '$cross_module_clause'(Clause),
3618    !.
3619'$valid_clause'(Clause) :-
3620    Error = error(Formal, _),
3621    catch(prolog:sandbox_allowed_clause(Clause), Error, true),
3622    !,
3623    (   var(Formal)
3624    ->  true
3625    ;   print_message(error, Error),
3626        fail
3627    ).
3628'$valid_clause'(Clause) :-
3629    print_message(error,
3630                  error(permission_error(assert,
3631                                         sandboxed_clause,
3632                                         Clause), _)),
3633    fail.
3634
3635'$cross_module_clause'(Clause) :-
3636    '$head_module'(Clause, Module),
3637    \+ '$current_source_module'(Module).
3638
3639'$head_module'(Var, _) :-
3640    var(Var), !, fail.
3641'$head_module'((Head :- _), Module) :-
3642    '$head_module'(Head, Module).
3643'$head_module'(Module:_, Module).
3644
3645'$clause_source'('$source_location'(File,Line):Clause, Clause, File:Line) :- !.
3646'$clause_source'(Clause, Clause, -).
3647
3648%!  '$store_clause'(+Term, +Id) is det.
3649%
3650%   This interface is used by PlDoc (and who knows).  Kept for to avoid
3651%   compatibility issues.
3652
3653:- public
3654    '$store_clause'/2.
3655
3656'$store_clause'(Term, Id) :-
3657    '$clause_source'(Term, Clause, SrcLoc),
3658    '$store_clause'(Clause, _, Id, SrcLoc).
3659
3660%!  compile_aux_clauses(+Clauses) is det.
3661%
3662%   Compile clauses given the current  source   location  but do not
3663%   change  the  notion  of   the    current   procedure  such  that
3664%   discontiguous  warnings  are  not  issued.    The   clauses  are
3665%   associated with the current file and  therefore wiped out if the
3666%   file is reloaded.
3667%
3668%   If the cross-referencer is active, we should not (re-)assert the
3669%   clauses.  Actually,  we  should   make    them   known   to  the
3670%   cross-referencer. How do we do that?   Maybe we need a different
3671%   API, such as in:
3672%
3673%     ==
3674%     expand_term_aux(Goal, NewGoal, Clauses)
3675%     ==
3676%
3677%   @tbd    Deal with source code layout?
3678
3679compile_aux_clauses(_Clauses) :-
3680    current_prolog_flag(xref, true),
3681    !.
3682compile_aux_clauses(Clauses) :-
3683    source_location(File, _Line),
3684    '$compile_aux_clauses'(Clauses, File).
3685
3686'$compile_aux_clauses'(Clauses, File) :-
3687    setup_call_cleanup(
3688        '$start_aux'(File, Context),
3689        '$store_aux_clauses'(Clauses, File),
3690        '$end_aux'(File, Context)).
3691
3692'$store_aux_clauses'(Clauses, File) :-
3693    is_list(Clauses),
3694    !,
3695    forall('$member'(C,Clauses),
3696           '$compile_term'(C, _Layout, File)).
3697'$store_aux_clauses'(Clause, File) :-
3698    '$compile_term'(Clause, _Layout, File).
3699
3700
3701		 /*******************************
3702		 *            STAGING		*
3703		 *******************************/
3704
3705%!  '$stage_file'(+Target, -Stage) is det.
3706%!  '$install_staged_file'(+Catcher, +Staged, +Target, +OnError).
3707%
3708%   Create files using _staging_, where we  first write a temporary file
3709%   and move it to Target if  the   file  was created successfully. This
3710%   provides an atomic transition, preventing  customers from reading an
3711%   incomplete file.
3712
3713'$stage_file'(Target, Stage) :-
3714    file_directory_name(Target, Dir),
3715    file_base_name(Target, File),
3716    current_prolog_flag(pid, Pid),
3717    format(atom(Stage), '~w/.~w.~d', [Dir,File,Pid]).
3718
3719'$install_staged_file'(exit, Staged, Target, error) :-
3720    !,
3721    rename_file(Staged, Target).
3722'$install_staged_file'(exit, Staged, Target, OnError) :-
3723    !,
3724    InstallError = error(_,_),
3725    catch(rename_file(Staged, Target),
3726          InstallError,
3727          '$install_staged_error'(OnError, InstallError, Staged, Target)).
3728'$install_staged_file'(_, Staged, _, _OnError) :-
3729    E = error(_,_),
3730    catch(delete_file(Staged), E, true).
3731
3732'$install_staged_error'(OnError, Error, Staged, _Target) :-
3733    E = error(_,_),
3734    catch(delete_file(Staged), E, true),
3735    (   OnError = silent
3736    ->  true
3737    ;   OnError = fail
3738    ->  fail
3739    ;   print_message(warning, Error)
3740    ).
3741
3742
3743                 /*******************************
3744                 *             READING          *
3745                 *******************************/
3746
3747:- multifile
3748    prolog:comment_hook/3.                  % hook for read_clause/3
3749
3750
3751                 /*******************************
3752                 *       FOREIGN INTERFACE      *
3753                 *******************************/
3754
3755%       call-back from PL_register_foreign().  First argument is the module
3756%       into which the foreign predicate is loaded and second is a term
3757%       describing the arguments.
3758
3759:- dynamic
3760    '$foreign_registered'/2.
3761
3762                 /*******************************
3763                 *   TEMPORARY TERM EXPANSION   *
3764                 *******************************/
3765
3766% Provide temporary definitions for the boot-loader.  These are replaced
3767% by the real thing in load.pl
3768
3769:- dynamic
3770    '$expand_goal'/2,
3771    '$expand_term'/4.
3772
3773'$expand_goal'(In, In).
3774'$expand_term'(In, Layout, In, Layout).
3775
3776
3777                 /*******************************
3778                 *         TYPE SUPPORT         *
3779                 *******************************/
3780
3781'$type_error'(Type, Value) :-
3782    (   var(Value)
3783    ->  throw(error(instantiation_error, _))
3784    ;   throw(error(type_error(Type, Value), _))
3785    ).
3786
3787'$domain_error'(Type, Value) :-
3788    throw(error(domain_error(Type, Value), _)).
3789
3790'$existence_error'(Type, Object) :-
3791    throw(error(existence_error(Type, Object), _)).
3792
3793'$permission_error'(Action, Type, Term) :-
3794    throw(error(permission_error(Action, Type, Term), _)).
3795
3796'$instantiation_error'(_Var) :-
3797    throw(error(instantiation_error, _)).
3798
3799'$uninstantiation_error'(NonVar) :-
3800    throw(error(uninstantiation_error(NonVar), _)).
3801
3802'$must_be'(list, X) :- !,
3803    '$skip_list'(_, X, Tail),
3804    (   Tail == []
3805    ->  true
3806    ;   '$type_error'(list, Tail)
3807    ).
3808'$must_be'(options, X) :- !,
3809    (   '$is_options'(X)
3810    ->  true
3811    ;   '$type_error'(options, X)
3812    ).
3813'$must_be'(atom, X) :- !,
3814    (   atom(X)
3815    ->  true
3816    ;   '$type_error'(atom, X)
3817    ).
3818'$must_be'(integer, X) :- !,
3819    (   integer(X)
3820    ->  true
3821    ;   '$type_error'(integer, X)
3822    ).
3823'$must_be'(between(Low,High), X) :- !,
3824    (   integer(X)
3825    ->  (   between(Low, High, X)
3826        ->  true
3827        ;   '$domain_error'(between(Low,High), X)
3828        )
3829    ;   '$type_error'(integer, X)
3830    ).
3831'$must_be'(callable, X) :- !,
3832    (   callable(X)
3833    ->  true
3834    ;   '$type_error'(callable, X)
3835    ).
3836'$must_be'(acyclic, X) :- !,
3837    (   acyclic_term(X)
3838    ->  true
3839    ;   '$domain_error'(acyclic_term, X)
3840    ).
3841'$must_be'(oneof(Type, Domain, List), X) :- !,
3842    '$must_be'(Type, X),
3843    (   memberchk(X, List)
3844    ->  true
3845    ;   '$domain_error'(Domain, X)
3846    ).
3847'$must_be'(boolean, X) :- !,
3848    (   (X == true ; X == false)
3849    ->  true
3850    ;   '$type_error'(boolean, X)
3851    ).
3852'$must_be'(ground, X) :- !,
3853    (   ground(X)
3854    ->  true
3855    ;   '$instantiation_error'(X)
3856    ).
3857'$must_be'(filespec, X) :- !,
3858    (   (   atom(X)
3859        ;   string(X)
3860        ;   compound(X),
3861            compound_name_arity(X, _, 1)
3862        )
3863    ->  true
3864    ;   '$type_error'(filespec, X)
3865    ).
3866
3867% Use for debugging
3868%'$must_be'(Type, _X) :- format('Unknown $must_be type: ~q~n', [Type]).
3869
3870
3871                /********************************
3872                *       LIST PROCESSING         *
3873                *********************************/
3874
3875'$member'(El, [H|T]) :-
3876    '$member_'(T, El, H).
3877
3878'$member_'(_, El, El).
3879'$member_'([H|T], El, _) :-
3880    '$member_'(T, El, H).
3881
3882
3883'$append'([], L, L).
3884'$append'([H|T], L, [H|R]) :-
3885    '$append'(T, L, R).
3886
3887'$select'(X, [X|Tail], Tail).
3888'$select'(Elem, [Head|Tail], [Head|Rest]) :-
3889    '$select'(Elem, Tail, Rest).
3890
3891'$reverse'(L1, L2) :-
3892    '$reverse'(L1, [], L2).
3893
3894'$reverse'([], List, List).
3895'$reverse'([Head|List1], List2, List3) :-
3896    '$reverse'(List1, [Head|List2], List3).
3897
3898'$delete'([], _, []) :- !.
3899'$delete'([Elem|Tail], Elem, Result) :-
3900    !,
3901    '$delete'(Tail, Elem, Result).
3902'$delete'([Head|Tail], Elem, [Head|Rest]) :-
3903    '$delete'(Tail, Elem, Rest).
3904
3905'$last'([H|T], Last) :-
3906    '$last'(T, H, Last).
3907
3908'$last'([], Last, Last).
3909'$last'([H|T], _, Last) :-
3910    '$last'(T, H, Last).
3911
3912
3913%!  length(?List, ?N)
3914%
3915%   Is true when N is the length of List.
3916
3917:- '$iso'((length/2)).
3918
3919length(List, Length) :-
3920    var(Length),
3921    !,
3922    '$skip_list'(Length0, List, Tail),
3923    (   Tail == []
3924    ->  Length = Length0                    % +,-
3925    ;   var(Tail)
3926    ->  Tail \== Length,                    % avoid length(L,L)
3927        '$length3'(Tail, Length, Length0)   % -,-
3928    ;   throw(error(type_error(list, List),
3929                    context(length/2, _)))
3930    ).
3931length(List, Length) :-
3932    integer(Length),
3933    Length >= 0,
3934    !,
3935    '$skip_list'(Length0, List, Tail),
3936    (   Tail == []                          % proper list
3937    ->  Length = Length0
3938    ;   var(Tail)
3939    ->  Extra is Length-Length0,
3940        '$length'(Tail, Extra)
3941    ;   throw(error(type_error(list, List),
3942                    context(length/2, _)))
3943    ).
3944length(_, Length) :-
3945    integer(Length),
3946    !,
3947    throw(error(domain_error(not_less_than_zero, Length),
3948                context(length/2, _))).
3949length(_, Length) :-
3950    throw(error(type_error(integer, Length),
3951                context(length/2, _))).
3952
3953'$length3'([], N, N).
3954'$length3'([_|List], N, N0) :-
3955    N1 is N0+1,
3956    '$length3'(List, N, N1).
3957
3958
3959                 /*******************************
3960                 *       OPTION PROCESSING      *
3961                 *******************************/
3962
3963%!  '$is_options'(@Term) is semidet.
3964%
3965%   True if Term looks like it provides options.
3966
3967'$is_options'(Map) :-
3968    is_dict(Map, _),
3969    !.
3970'$is_options'(List) :-
3971    is_list(List),
3972    (   List == []
3973    ->  true
3974    ;   List = [H|_],
3975        '$is_option'(H, _, _)
3976    ).
3977
3978'$is_option'(Var, _, _) :-
3979    var(Var), !, fail.
3980'$is_option'(F, Name, Value) :-
3981    functor(F, _, 1),
3982    !,
3983    F =.. [Name,Value].
3984'$is_option'(Name=Value, Name, Value).
3985
3986%!  '$option'(?Opt, +Options) is semidet.
3987
3988'$option'(Opt, Options) :-
3989    is_dict(Options),
3990    !,
3991    [Opt] :< Options.
3992'$option'(Opt, Options) :-
3993    memberchk(Opt, Options).
3994
3995%!  '$option'(?Opt, +Options, +Default) is det.
3996
3997'$option'(Term, Options, Default) :-
3998    arg(1, Term, Value),
3999    functor(Term, Name, 1),
4000    (   is_dict(Options)
4001    ->  (   get_dict(Name, Options, GVal)
4002        ->  Value = GVal
4003        ;   Value = Default
4004        )
4005    ;   functor(Gen, Name, 1),
4006        arg(1, Gen, GVal),
4007        (   memberchk(Gen, Options)
4008        ->  Value = GVal
4009        ;   Value = Default
4010        )
4011    ).
4012
4013%!  '$select_option'(?Opt, +Options, -Rest) is semidet.
4014%
4015%   Select an option from Options.
4016%
4017%   @arg Rest is always a map.
4018
4019'$select_option'(Opt, Options, Rest) :-
4020    select_dict([Opt], Options, Rest).
4021
4022%!  '$merge_options'(+New, +Default, -Merged) is det.
4023%
4024%   Add/replace options specified in New.
4025%
4026%   @arg Merged is always a map.
4027
4028'$merge_options'(New, Old, Merged) :-
4029    put_dict(New, Old, Merged).
4030
4031
4032                 /*******************************
4033                 *   HANDLE TRACER 'L'-COMMAND  *
4034                 *******************************/
4035
4036:- public '$prolog_list_goal'/1.
4037
4038:- multifile
4039    user:prolog_list_goal/1.
4040
4041'$prolog_list_goal'(Goal) :-
4042    user:prolog_list_goal(Goal),
4043    !.
4044'$prolog_list_goal'(Goal) :-
4045    use_module(library(listing), [listing/1]),
4046    @(listing(Goal), user).
4047
4048
4049                 /*******************************
4050                 *             HALT             *
4051                 *******************************/
4052
4053:- '$iso'((halt/0)).
4054
4055halt :-
4056    halt(0).
4057
4058
4059%!  at_halt(:Goal)
4060%
4061%   Register Goal to be called if the system halts.
4062%
4063%   @tbd: get location into the error message
4064
4065:- meta_predicate at_halt(0).
4066:- dynamic        system:term_expansion/2, '$at_halt'/2.
4067:- multifile      system:term_expansion/2, '$at_halt'/2.
4068
4069system:term_expansion((:- at_halt(Goal)),
4070                      system:'$at_halt'(Module:Goal, File:Line)) :-
4071    \+ current_prolog_flag(xref, true),
4072    source_location(File, Line),
4073    '$current_source_module'(Module).
4074
4075at_halt(Goal) :-
4076    asserta('$at_halt'(Goal, (-):0)).
4077
4078:- public '$run_at_halt'/0.
4079
4080'$run_at_halt' :-
4081    forall(clause('$at_halt'(Goal, Src), true, Ref),
4082           ( '$call_at_halt'(Goal, Src),
4083             erase(Ref)
4084           )).
4085
4086'$call_at_halt'(Goal, _Src) :-
4087    catch(Goal, E, true),
4088    !,
4089    (   var(E)
4090    ->  true
4091    ;   subsumes_term(cancel_halt(_), E)
4092    ->  '$print_message'(informational, E),
4093        fail
4094    ;   '$print_message'(error, E)
4095    ).
4096'$call_at_halt'(Goal, _Src) :-
4097    '$print_message'(warning, goal_failed(at_halt, Goal)).
4098
4099%!  cancel_halt(+Reason)
4100%
4101%   This predicate may be called from   at_halt/1 handlers to cancel
4102%   halting the program. If  causes  halt/0   to  fail  rather  than
4103%   terminating the process.
4104
4105cancel_halt(Reason) :-
4106    throw(cancel_halt(Reason)).
4107
4108
4109                /********************************
4110                *      LOAD OTHER MODULES       *
4111                *********************************/
4112
4113:- meta_predicate
4114    '$load_wic_files'(:).
4115
4116'$load_wic_files'(Files) :-
4117    Files = Module:_,
4118    '$execute_directive'('$set_source_module'(OldM, Module), []),
4119    '$save_lex_state'(LexState, []),
4120    '$style_check'(_, 0xC7),                % see style_name/2 in syspred.pl
4121    '$compilation_mode'(OldC, wic),
4122    consult(Files),
4123    '$execute_directive'('$set_source_module'(OldM), []),
4124    '$execute_directive'('$restore_lex_state'(LexState), []),
4125    '$set_compilation_mode'(OldC).
4126
4127
4128%!  '$load_additional_boot_files' is det.
4129%
4130%   Called from compileFileList() in pl-wic.c.   Gets the files from
4131%   "-c file ..." and loads them into the module user.
4132
4133:- public '$load_additional_boot_files'/0.
4134
4135'$load_additional_boot_files' :-
4136    current_prolog_flag(argv, Argv),
4137    '$get_files_argv'(Argv, Files),
4138    (   Files \== []
4139    ->  format('Loading additional boot files~n'),
4140        '$load_wic_files'(user:Files),
4141        format('additional boot files loaded~n')
4142    ;   true
4143    ).
4144
4145'$get_files_argv'([], []) :- !.
4146'$get_files_argv'(['-c'|Files], Files) :- !.
4147'$get_files_argv'([_|Rest], Files) :-
4148    '$get_files_argv'(Rest, Files).
4149
4150'$:-'(('$boot_message'('Loading Prolog startup files~n', []),
4151       source_location(File, _Line),
4152       file_directory_name(File, Dir),
4153       atom_concat(Dir, '/load.pl', LoadFile),
4154       '$load_wic_files'(system:[LoadFile]),
4155       (   current_prolog_flag(windows, true)
4156       ->  atom_concat(Dir, '/menu.pl', MenuFile),
4157           '$load_wic_files'(system:[MenuFile])
4158       ;   true
4159       ),
4160       '$boot_message'('SWI-Prolog boot files loaded~n', []),
4161       '$compilation_mode'(OldC, wic),
4162       '$execute_directive'('$set_source_module'(user), []),
4163       '$set_compilation_mode'(OldC)
4164      )).
4165