1% ----------------------------------------------------------------------
2% BEGIN LICENSE BLOCK
3% Version: CMPL 1.1
4%
5% The contents of this file are subject to the Cisco-style Mozilla Public
6% License Version 1.1 (the "License"); you may not use this file except
7% in compliance with the License.  You may obtain a copy of the License
8% at www.eclipse-clp.org/license.
9%
10% Software distributed under the License is distributed on an "AS IS"
11% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.  See
12% the License for the specific language governing rights and limitations
13% under the License.
14%
15% The Original Code is  The ECLiPSe Constraint Logic Programming System.
16% The Initial Developer of the Original Code is  Cisco Systems, Inc.
17% Portions created by the Initial Developer are
18% Copyright (C) 1999-2006 Cisco Systems, Inc.  All Rights Reserved.
19%
20% Contributor(s): IC-Parc, Imperal College London
21%
22% END LICENSE BLOCK
23%
24%
25% ECLiPSe II debugger -- Tcl/Tk Interface
26%
27% System:	ECLiPSe Constraint Logic Programming System
28% Version:	$Id: tracer_tcl.pl,v 1.14 2013/02/23 00:23:18 jschimpf Exp $
29% Authors:	Joachim Schimpf, IC-Parc
30%		Kish Shen, IC-Parc
31%               Josh Singer, Parc Technologies
32%
33%----------------------------------------------------------------------
34
35:- module(tracer_tcl).
36
37:- local
38	struct(inspect(type,top,path,written,module)),
39	struct(dg_filter(traceonly, spiedonly, wakeonly)),
40	struct(mfile(dir,module,file)),
41	struct(minfo(interface,comments)),
42
43	reference(exec_state),
44	reference(matdisplaydata),
45
46	variable(observed_count),
47        variable(filter_spy_goal),
48        variable(filter_status),
49        variable(filter_count),
50        variable(filter_hits),
51        variable(inspect_observed),
52	variable(next_cmd),
53	variable(indent_step),
54	variable(dbg_format_string),
55	variable(dbg_goal_format_string),
56	variable(dbg_print_depth),
57        variable(matdisplayid),
58	variable(lbpath_type),
59	variable(library_info),
60	variable(show_module),
61        variable(tracer_command).
62
63
64
65:- setval(matdisplayid,0).
66:- setval(observed_count,0).
67:- setval(inspect_observed, []).
68:- setval(filter_spy_goal, none).
69:- setval(filter_status, off).
70:- setval(filter_count, 1).
71:- setval(filter_hits, 0).
72:- setval(tracer_command, "c").
73
74% the types for each level of the path used by the library browser
75:- setval(lbpath_type, [top,dir,module,interface]).
76
77:- export
78        init_library_info/0,
79	init_toplevel_module/0,
80	expand_lbnode/2,
81	lbnode_display/3,
82	lbnode_info/4,
83	lbnode_loadmodule/1,
84	return_html_root/1,
85        report_stats/2,
86	stop_report_stats/0,
87	start_report_stats_text_summary/1,
88	stop_report_stats_text_summary/0,
89	change_report_interval/1,
90	inspect_command/3,
91	inspect_get_children_for_path/5,
92	compile_string/1,
93	get_tracer_output_modes/1, set_tracer_output_modes/1,
94	get_tracer_print_depth/1, set_tracer_print_depth/1,
95	compile_os_file/2,
96	use_module_os/2,
97	list_files/1,
98	list_modules/2,
99	list_predicates/4,
100	set_flag_string/4,
101	gui_help_string/2,
102	gui_dg/3,
103        get_triggers/1,
104        get_source_info/4,
105	flag_value/4,
106	record_source_file/1,
107	register_inspected_term/2,
108	make_display_matrix_body/6,
109	make_display_matrix_body/3,
110	kill_display_matrix_body/2,
111	install_guitools/0,
112	uninstall_guitools/0,
113	get_ancestors/1,
114        get_current_traceline/4,
115	get_goal_info_by_invoc/8,
116        prepare_filter/1,
117        set_usepred_info/5,
118        reenable_usepred/0,
119        set_tracer_command/1,
120	toggle_source_breakpoint/5,
121	file_is_readable/1,
122	read_file_for_gui/1,
123        breakpoints_for_file/4,
124	find_exact_callinfo/3,
125	find_matching_callinfo/4,
126        is_current_goal/2,
127	saros_get_library_path/1,
128	saros_set_library_path/1,
129	saros_compile/1,
130	saros_fcompile/2,
131	saros_icompile/2,
132        saros_eci_to_html/3,
133        saros_ecis_to_htmls/4,
134	saros_cd/1,
135	saros_use_module/1,
136	saros_get_goal_info_by_invoc/10.
137
138:- reexport
139        current_files_with_port_lines/1
140   from sepia_kernel.
141
142
143%----------------------------------------------------------------------
144:- pragma(system).
145:- pragma(nodebug).
146
147%:- import struct(tf), struct(trace_line) from sepia_kernel.
148:- import sepia_kernel.
149
150:- import
151	set_default_error_handler/2,
152	configure_prefilter/5,
153	cut_to_stamp/2,
154	trace_mode/2,
155	find_goal/3,
156	get_attribute/3,
157	get_tf_prop/3,
158	current_predicate_with_port/4,
159        get_portlist_from_file/4,
160        find_matching_breakport/6,
161        stack_overflow_message/1
162    from sepia_kernel.
163
164:- lib(development_support).
165
166:- setval(dbg_goal_format_string, "%*GQPmw").
167:- setval(dbg_print_depth, 5).
168:- setval(show_module, off).
169:- setval(indent_step, 0).
170
171
172%----------------------------------------------------------------------
173% Tracer/GUI interface
174% We use two queues to communicate with the GUI
175%----------------------------------------------------------------------
176
177trace_start_handler_tcl :-
178	setval(filter_status, off),
179        setval(filter_hits, 0),
180        setval(filter_counts, 1),
181        ( peer_queue_get_property(debug_traceline, peer_name, Name),
182          peer_get_property(Name, language, "java") ->
183            /* this is a hopefully temporary way to detect we are using Saros
184               and avoid making changes to the Java side for now
185            */
186            true
187        ;
188            % inform GUI of start of tracing
189            write_exdr(debug_traceline, []),
190            flush(debug_traceline)
191        ).
192
193
194trace_line_handler_tcl(_, Current) :-
195        % call the goal_filter goal, which represents the user's "conditional
196        % breakpoint. Do this FIRST for speed!!!
197        getval(filter_status, FilterStatus),
198        do_filter(FilterStatus, Current),
199	% make sure the debug_traceline stream is usable, otherwise fail
200	(get_stream_info(debug_traceline, usable, on) ->
201	    % usable off only applies if stream performs a yield/2, but for
202            % remote peer queues, this yeild is not done, and can be handled
203	    true ; peer_queue_get_property(debug_traceline, peer_type, remote)
204	),
205	% store the current trace line in a global reference, where it
206	% can be picked up by some of the interactive tools
207	setval(exec_state, Current),
208	% if the goal_filter goal succeeds, we do not want to use the
209        % other clause of trace_line_handler_tcl, which does nothing.
210	!,
211	flush(debug_output),
212	open(string(""), write, SS),
213	make_trace_line(SS, Current, Depth, Port, Invoc, Prio, FPath, _Linum, From0, To0),
214	get_stream_info(SS, name, Line),
215	close(SS),
216	port_style(Port, Style),
217        check_if_source_should_update(Port, From0, To0, From, To),
218	write_exdr(debug_traceline, [Depth, Style, Line, Invoc, Port, Prio,
219                                     FPath, From, To]),
220	flush(debug_traceline),	% may not work in nested emulator...
221        peer_do_multitask(tracer),
222        getval(tracer_command, Cmd),
223	%writeln(error, got:Cmd),
224	%writeln(error, current:Current),
225	interpret_command(Cmd, Current, Depth, Cont),
226	call(Cont),	% may cut_to/fail
227	getval(inspect_observed, Obs),
228	setval(inspect_observed, []),  % reset
229	create_observed(Obs, Current).
230trace_line_handler_tcl(_,_).
231
232% Prepare for the filter command
233prepare_filter(Count) :-
234        setval(filter_status, on),
235        setval(filter_count, Count).
236
237do_filter(off, _).
238do_filter(on, _) :-
239        filter_count_and_reset(none).
240do_filter(goal(SpyStatus), trace_line{frame:tf{goal:Goal, module:Module}}):-
241        goal_filter(Goal, Module),
242        filter_count_and_reset(SpyStatus).
243
244filter_count_and_reset(SpyStatus) :-
245        incval(filter_hits),
246        getval(filter_count, FCount),
247        (FCount > 1 ->
248             decval(filter_count),
249             fail
250        ;
251             reset_usepred_info(SpyStatus)
252        ),
253        setval(filter_status, off).
254
255make_trace_line(Stream, trace_line{port:Port, frame:Frame}, Depth,
256                Port, Invoc, Prio, FPath, Linum, From, To) :-
257	Frame = tf{invoc:Invoc,goal:Goal,depth:Depth,prio:Prio,module:M,
258                         path:Path0, line:Linum, from:From, to:To},
259	register_inspected_term(Goal, M),
260        % wrapper around pathname to avoid empty string
261        (Path0 == '' ->
262            FPath = no
263        ;
264            os_file_name(Path0, OSPath),
265            FPath = p(OSPath)
266        ),
267        % print priority only if not the normal 12
268        (Prio == 12 -> PrioS = "" ; concat_string([<,Prio,>], PrioS)),
269	( get_tf_prop(Frame, skip, on) -> Prop = 0'S ; Prop = 0'  ),
270	( get_tf_prop(Frame, break) =\= 0 -> Spied = 0'#
271	; get_tf_prop(Frame, spy, on) -> Spied = 0'+ ; Spied = 0'  ),
272	Indent is Depth*getval(indent_step),
273	printf(Stream, "%c%c%*c(%d) %d %A%s  ",
274			[Prop, Spied, Indent, 0' , Invoc, Depth, Port, PrioS]),
275	( getval(show_module,on) -> MGoal = Goal@M ; MGoal = Goal ),
276	getval(dbg_goal_format_string, Format),
277	getval(dbg_print_depth, PDepth),
278	printf(Stream, Format, [PDepth,MGoal])@M.
279
280
281:- mode port_style(+,-).
282port_style(fail, "fail_style") :- !.
283% Next line leads to not printing LEAVE in stack display. Use separate style?
284%port_style(leave, "fail_style") :- !.
285port_style(exit, "exit_style") :- !.
286port_style('*exit', "exit_style") :- !.
287port_style(_, "call_style").
288
289% set From/To to -1 if source display should not be updated to new positions
290:- mode check_if_source_should_update(+,+,+,-,-).
291check_if_source_should_update(next, _, _, -1, -1) :- !.
292check_if_source_should_update(else, _, _, -1, -1) :- !.
293check_if_source_should_update(_, From, To, From, To).
294
295set_tracer_command(Cmd) :-
296        setval(tracer_command, Cmd).
297
298:- mode interpret_command(+,+,+,-).
299interpret_command("a", CurrentPort, _, Cont) :- !,
300	trace_mode(5, 0),
301	( CurrentPort = trace_line{port:leave} ->
302	    % don't abort, we may not have any catching block!
303	    % turn it into a creep instead...
304	    Cont = true
305	;
306	    Cont = abort
307	).
308interpret_command("l", _, _, true) :- !, trace_mode(2, []).
309interpret_command("filter", _, _, true) :- !. % filter set, just continue
310interpret_command("s", _, Depth, true) :- !, trace_mode(3, Depth).
311interpret_command("n", _, _, true) :- !, trace_mode(5, 0).
312interpret_command("N", _, _, true) :- !, trace_mode(5, 0),
313	set_flag(debugging,nodebug).
314interpret_command("c", _, _, true) :- !, trace_mode(0, []).
315interpret_command("i", _, _, true) :- !.
316interpret_command("j", _, _, true) :- !.
317interpret_command(f(N), Current, _, Cont) :- !,
318	Current = trace_line{port:Port,frame:Stack},
319	( Port \== fail, Port \== leave, find_goal(N, Stack, Frame) ->
320	    Cont = (cut_to_stamp(Frame, chp of tf),fail)
321	;
322	    Cont = true		% already failing or frame not found
323	).
324interpret_command("z", Current, _, true) :- !,	% zap to different port
325	Current = trace_line{port:Port},
326	configure_prefilter(_, _, ~Port, _, dont_care).
327interpret_command("", _, _, true) :- !.	% no command, continue as before
328
329
330%----------------------------------------------------------------------
331% Filter goal setup
332%----------------------------------------------------------------------
333
334% initial (empty) breakpoint condition
335goal_filter(_,_).
336
337reset_usepred_info(none) :- !.
338reset_usepred_info(PreviousSpyStatus) :-
339        % there is an active filter goal....
340        % set the spy status of the template predicate to its previous
341        % state
342        % if no defining module, set on whatever predicate is visible
343        % from here
344        getval(filter_spy_goal,DefiningModule:TemplatePredSpec),
345        (var(DefiningModule) ->
346             set_flag(TemplatePredSpec, spy, PreviousSpyStatus)
347        ;
348             set_flag(TemplatePredSpec, spy, PreviousSpyStatus)@DefiningModule
349        ).
350
351
352set_usepred_info(PredMatchString, PredModuleString, PredDefModuleString, PredConditionString, Status):-
353	!,
354	% parse the defining module
355	term_string(DefiningModule, PredDefModuleString),
356	% construct and compile the condition
357        concat_string(["goal_filter((", PredMatchString,
358		"),(", PredModuleString,
359	        ")) ?- catch(\\+(\\+ (", PredConditionString,")), _, fail)"],
360	    PredMatchConditionString),
361        % DefiningModule cannot be a variable!
362	term_string(NewGoalFilter, PredMatchConditionString)@DefiningModule,
363	compile_term(NewGoalFilter),
364	% Find the template's functor and arity
365	NewGoalFilter = (goal_filter(Template,_) ?- _),
366	(var(Template) ->
367	    Status = none,
368            setval(filter_status, goal(none)),
369	    setval(filter_spy_goal, none)
370	;
371	    functor(Template, TemplateFunctor, TemplateArity),
372	    % compose its PredSpec out of this.
373	    TemplatePredSpec = TemplateFunctor/TemplateArity,
374	    % get the spy status of the template predicate
375	    % if no defining module, look at whatever is visible from here
376	    % fails if module or predicate does not currently exist
377            (set_spy_status(TemplatePredSpec, DefiningModule) ->
378                 % record the PredSpec, DefiningModule in a local variable
379                 setval(filter_spy_goal,DefiningModule:TemplatePredSpec),
380                 Status = spy_set
381            ;
382                 setval(filter_spy_goal, none),
383                 setval(filter_status, off),
384                 Status = not_found
385            )
386        ).
387
388
389set_spy_status(TemplatePredSpec, DefiningModule) :-
390        find_pred_spyinfo(TemplatePredSpec, DefiningModule, PreviousSpyStatus),
391        setval(filter_status, goal(PreviousSpyStatus)),
392        % set a spypoint on the template predicate
393        % The point of this is that if the template option is
394        % used , and a spypoint is put on the template predicate,
395        % no other predicates need be examined, only spied ones.
396        % Therefore an efficiency advantage is gained.
397        % if no defining module, set one on whatever is visible from here
398        set_flag(TemplatePredSpec, spy, on)@DefiningModule.
399
400reenable_usepred :-
401        (getval(filter_spy_goal, DefiningModule:PredSpec) ->
402             % set_spy_status/2 should not fail here as PredSpec must exist
403             set_spy_status(PredSpec, DefiningModule)
404        ;
405             setval(filter_status, goal(none))
406        ).
407
408find_pred_spyinfo(TemplatePredSpec, DefiningModule, PreviousSpyStatus) :-
409	(var(DefiningModule) ->
410	     get_flag(TemplatePredSpec, spy, PreviousSpyStatus)
411        ;
412	     current_module(DefiningModule),
413	     get_flag(TemplatePredSpec, spy, PreviousSpyStatus)@DefiningModule
414	).
415
416%----------------------------------------------------------------------
417% Inspect subterm stuff
418%----------------------------------------------------------------------
419
420:- local reference(inspect_object).
421
422
423% eventually these will allow inspect of more than one term
424register_inspected_term(Term, Module) :-
425	setval(inspect_object, f(Term, Module)),
426	true.
427
428
429get_inspected_term(current, Term, Module) :-
430	( getval(inspect_object, f(Term,Module)) ->
431	    true
432	;
433	    Term = 'No term registered for inspection',
434	    get_flag(toplevel_module, Module)
435	).
436get_inspected_term(invoc(N), Goal, Module) :-
437	find_goal_by_invoc(N, _LookupModule, Goal, Module, _, _, _, _).
438get_inspected_term(display(I,R,C), Term, Module) :-
439	get_matrix_term(I, R, C, Term, Module).
440
441
442inspect_command(SourceS, Command, Reply) :-
443	term_string(Source, SourceS),
444	get_inspected_term(Source, Term, Module),
445	process_inspect_command(Command, Term, Reply, Module).
446
447
448process_inspect_command(end, _Term, _, _M) ?- !.
449% exit inspect term. Nothing to be done for now
450process_inspect_command(info(Depth,["1"|Path]), Term, Reply, M) ?- !,
451% provides a normal printable version and summary of the term with Path.
452% merged previous summary and display commands; this allows more flexibility
453% on the Tcl side for how subterms are processed.
454	provide_subterm(Path, Depth, Term, Reply, M).
455process_inspect_command(record_observed(SSource,["1"|Path],Label), _, _Reply, _M) ?- !,
456% make a record of a term that is to be observed
457	term_string(Source, SSource),
458	getval(inspect_observed, ToBeObs),
459	setval(inspect_observed, [o(Source,Path,Label)|ToBeObs]).
460process_inspect_command(movepath(up,N,["1"|Path]), _Term, Reply, _M) ?- !,
461% moves the current subterm up
462	reverse(Path, RPath),
463	move_up(N, RPath, Reply).
464process_inspect_command(movepath(Dir,N,["1"|Path]), Term, Reply, M) ?- !,
465% moves the current subterm to the left or right
466        move_sideways(Dir, N, Path, Term, Reply, M).
467process_inspect_command(select(SourceS), _, Reply, _) ?- !,
468% change the inspected item (in Tcl; here just checks that item is valid)
469	term_string(Source, SourceS),
470	(get_inspected_term(Source, _, _) ->
471	    Reply = "ok" ; Reply = "failed"
472        ).
473process_inspect_command(childnodes(Type,Arity,LSize,["1"|Path]), Term, Reply, Module) ?- !,
474/* returns  `position' information for the children of all inspector's nodes
475  These nodes are of different ECLiPSe types, and the `positions'
476  can be in a special format which can then be used both by the
477  Tcl and Prolog sides to take special actions in interpreting the path
478  and presenting the children.
479  Type + Arity are the type and arity of the node; LSize is the current
480  threshold length for treating list specially
481*/
482	provide_childnodes(Type, Path, Term, Arity, LSize, Reply, Module).
483process_inspect_command(modify(PositionS), _, Modifier, _) ?- !,
484/* checks to see if the name of an item needs to be modified because of its
485   position
486*/
487        term_string(Position, PositionS),
488	position_modifier(Position, Modifier).
489process_inspect_command(translate(PositionS), _, Reply, _) ?-
490/* translate a special position in the internal path format (e.g. 1=foo) to
491   a more readable format for Tcl to print out (e.g. 1 (filedname: foo)
492*/
493	term_string(Pos, PositionS),
494	translate_pos(Pos, Reply).
495
496
497translate_pos(N=Field, Out) ?-
498	integer(N), !,
499	(integer(Field) ->
500	    concat_string(["structure arg#", N], Out)
501	;   concat_string(["Named structure arg, fieldname:", Field], Out)
502        ).
503translate_pos(N-Attr, Out) ?-
504	integer(N), !,
505        concat_string(["attribute name:", Attr], Out).
506translate_pos(list(N), Out) ?-
507	integer(N), !,
508	concat_string(["List element (pos: ", N, ")"], Out).
509translate_pos(tail(N), Out) ?-
510	integer(N), !,
511	concat_string(["List tail (pos: ", N, ")"], Out).
512translate_pos(N, Out) :-
513	integer(N), !,
514	concat_string(["structure arg#", N], Out).
515translate_pos(Pos, Out) :-
516	open("", string, S),
517	printf(S, "unknown position type: %w", [Pos]),
518	get_stream_info(S, name, Out),
519	close(S).
520
521
522provide_childnodes(attributed, Path, Term, _, _, Reply, Module) ?- !,
523	get_subterm_from_path(Path, Term, Term, AVar, Module),
524	valid_attributes_listing(AVar, Reply).
525provide_childnodes(ncompound, Path, Term, Arity, _, Reply, Module) ?- !,
526	get_subterm_from_path(Path, Term, Term, Sub, Module),
527	named_structure(Sub, Module, Defs, Arity),
528	(foreacharg(Name, Defs), count(I, 1, _), foreach(NameSpec, Reply) do
529            term_string(I=Name, NameSpec)
530	).
531provide_childnodes(list, Path, Term, _, LSize, Reply, Module) ?- !,
532	get_subterm_from_path(Path, Term, Term, Sub, Module),
533	provide_listnodes(Sub, 1, LSize, Reply, Module).
534provide_childnodes(compound, _Path, _Term, Arity, LSize, Reply, _Module) ?- !,
535	% just use arity given to avoid cost of finding subterm
536	(Arity > LSize ->  % add argument position if > LSize
537	    (for(I, 1, Arity), foreach(PosSpec, Reply) do
538		term_string(I=I, PosSpec)
539	    )
540	;   (for(I, 1, Arity), foreach(I, Reply) do true)
541        ).
542provide_childnodes(scheduled, _, _, _, _, Reply, _) ?- !,
543	Reply = [1].
544provide_childnodes(suspended, _, _, _, _, Reply, _) ?- !,
545	Reply = [1].
546provide_childnodes(exphandle, _, _, _, _, Reply, _) ?- !,
547        Reply = [1].
548provide_childnodes(_Others, _, _, _, _, Reply, _) :-
549	Reply = [].
550
551
552provide_listnodes(List, ListPos0, LSize, Reply, Module) :-
553	List = [_|Tail],
554	ListPos1 is ListPos0 + 1,
555	term_string(list(ListPos0), Pos0S),
556	get_type(Tail, TType, Module),
557	((TType == list, ListPos0 < LSize) ->
558	    Reply = [Pos0S|Reply1],
559	    provide_listnodes(Tail, ListPos1, LSize, Reply1, Module)
560	;   term_string(tail(ListPos1), Pos1S),
561	    Reply = [Pos0S,Pos1S]
562        ).
563
564move_sideways(Dir, N, Path, Term, Reply, Mod) :-
565	(get_parent_path(Path, PPath, Pos, ArgNo0) ->
566            get_subterm_from_path(PPath, Term, Term, Parent, Mod),
567	    (Dir == right -> ArgNo is ArgNo0 + N ; ArgNo is ArgNo0 - N),
568            get_sibling_arg(Pos, ArgNo, PPath, Parent, Mod, NewPath, Status)
569
570	; % can't get parent
571	    Status = "false"
572	),
573	(Status == "false" -> NewPath = Path ; true),
574	Reply = [Status,["1"|NewPath]]. % add back the root node
575
576
577get_sibling_arg(Pos=FName0, ArgNo0, PPath, Parent, Module, NewPath, Status) ?-
578% named (or large) structure
579	(named_structure(Parent, Module, Defs, Arity) ->
580	    get_arg(ArgNo0, Arity, ArgNo, Status),
581	    arg(ArgNo, Defs, FName),
582            % path position should always be a string
583            term_string(ArgNo=FName, PosSpec),
584	    append(PPath, [PosSpec], NewPath)
585	; integer(Pos),integer(FName0), Pos = FName0 ->
586            % a large structure displayed with argument positions
587            arity(Parent, Arity),
588            get_arg(ArgNo0, Arity, ArgNo, Status),
589            term_string(ArgNo=ArgNo, PosSpec),
590            append(PPath, [PosSpec], NewPath)
591        ;
592            % not named nor large structure
593	    Status = "false"
594	).
595get_sibling_arg(Pos, ArgNo, PPath, Parent, _, NewPath, Status) :-
596	(integer(Pos) ->
597            % normal structure
598            arity(Parent, A),
599            get_arg(ArgNo, A, N1, Status),
600            term_string(N1, PosSpec),
601            append(PPath, [PosSpec], NewPath)
602        ;
603            Status = "false"
604        ).
605
606get_arg(ArgNo, A, N1, Status) :-
607	(ArgNo >  A ->
608	    Status = "out",
609	    N1 = A
610	;ArgNo < 1 ->
611	    Status = "out",
612	    N1 = 1
613	;   N1 = ArgNo,
614	    Status = "true"
615	).
616
617get_parent_path([PosS], PPath, Full, ArgNo) ?-
618	term_string(Pos, PosS),
619	valid_pos(Pos, ArgNo), !,
620	PPath = [], Pos = Full.
621get_parent_path([N|Path], [N|PPath], Pos, ArgNo) :-
622	get_parent_path(Path, PPath, Pos, ArgNo).
623
624
625move_up(N, RPath, [Status,["1"|NewPath]]) :-
626	port_remove_levels(N, RPath, RNewPath, Status),
627	reverse(RNewPath, NewPath).
628
629
630provide_subterm(Path, Depth, Term, Reply, M) :-
631	get_flag(output_mode, OM),
632	(Path == [] -> %toplevel goal
633	    concat_string(["%*",OM,"w"], DF)
634	;   concat_string(["%*",OM,"Gw"], DF)
635        ),
636	get_subterm_from_path(Path, Term, Term, Sub, M),
637	open("", string, S),
638	printf(S, DF, [Depth, Sub]),
639	get_stream_info(S, name, Out),
640	close(S),
641	get_type(Sub, Type, M),
642	get_summary_info(Type, Sub, Arity, Summary),
643	Reply = [Out, Summary, Type, Arity].
644
645
646get_type(Sub, Type, M) :-
647	type_of(Sub, Type1),
648	refine_type(Type1, Sub, M, Type).
649
650refine_type(var, Var, _, Type) :- !,
651	(meta(Var) -> Type = attributed ; Type = var).
652refine_type(compound, C, M, Type) :- !,
653	( named_structure(C, M, _, _) ->
654	    % ncompound is a structure with field names
655	    Type = ncompound
656	; C  = [_|_] ->  % a list (may be non-proper)
657	    Type = list
658	;
659            Type = compound
660	).
661refine_type(goal, S, _, Type) :- !,
662	get_suspension_data(S, state, State),
663	(State == 0 ->
664	    Type = suspended
665	; State == 1 ->
666	    Type = scheduled
667	; Type = dead
668	).
669refine_type(handle, H, _, Type) :-
670        is_expandable_handle(H, _),
671        Type = exphandle.
672refine_type(Type, _Var, _, Type).
673
674
675% check that a particular arg position is valid
676valid_pos(N0, N) :- integer(N0), !, N = N0.
677valid_pos(N0=_, N) ?- integer(N0),  N = N0. %named struct (or large struct)
678%valid_pos(list(N0), N) ?- integer(N0), !, N = N0.
679%valid_pos(tail(N0), N) ?- integer(N0), !, N = N0.
680
681
682get_summary_info(Type, Term, A, Out) ?-
683	get_functorarity(Type, Term, F, A),
684	open("", string, S),
685	seek(S, end_of_file),
686	print_subterm(Type, S, F, A),
687	get_stream_info(S, name, Out),
688	close(S).
689
690
691print_subterm(attributed, S, V, _A) :- !,
692	write(S, V).
693print_subterm(var, S, V, _A) :- !,
694	write(S, V).
695print_subterm(_T, S, F, A) :-
696	writeq(S, F),
697	(A \== -1 ->  % a type with valid arity
698	   write(S, "/"),
699	   writeq(S, A)
700         ; true
701        ).
702
703
704get_functorarity(compound, Term, F, A) ?- !,
705	functor(Term, F, A).
706get_functorarity(ncompound, Term, F, A) ?- !,
707	functor(Term, F, A).
708get_functorarity(list, _, F, A) ?- !,
709	A =  2, F = '.'.
710get_functorarity(var, Var, F, A) ?- !,
711	A = -1, F = Var.
712get_functorarity(attributed, Var, F, A) ?- !,
713	A = -1, F = Var.
714get_functorarity(atom, Atom, F, A) ?- !,
715	A = 0, F = Atom.
716get_functorarity(integer, I, F, A) ?- !,
717	A = -1, F = I.
718get_functorarity(float, J, F, A) ?- !,
719	A = -1, J = F.
720get_functorarity(breal, J, F, A) ?- !,
721	A = -1, J = F.
722get_functorarity(rational, R, F, A) ?- !,
723	A = -1, R = F.
724get_functorarity(string, S, F, A) ?- !,
725	A = -1, S = F.
726get_functorarity(handle, S, F, A) ?- !,
727	A = -1, S = F.
728get_functorarity(Susp, S, F, A) :-
729	(Susp == suspended ; Susp == scheduled ; Susp == dead), !,
730	A = -1, S = F.
731get_functorarity(_Unk, S, F, A) :-
732/* unknown type, catch it to avoid failure */
733        A = -1, S = F.
734
735% This code should go somewhere else; but it is different from the
736% navigate subterm for the tty interface, because you can choose
737% somewhere else entirely on the tree.
738% get_subterm_from_path(+Path, +Top, +Current, -SubTerm, +Module)
739get_subterm_from_path([], Top, Current, Sub, Mod) ?- !,
740	written_term(Top, Current, Sub, Mod).
741get_subterm_from_path([PosS|Path], Top, Current, Sub, M) ?-
742	term_string(Pos, PosS),
743	(get_subterm_child(Pos, Current, Top, Child, M) ->
744	    get_subterm_from_path(Path, Top, Child, Sub, M)
745	  ; printf(error, "%n *** can't follow path %w in %w%n%b", [Pos,Current])
746        ).
747
748get_subterm_child(Pos-_AttName, AVar, _, Attribute, _Module) ?-
749% attribute of an attributed var AVar.
750	integer(Pos), !,
751	get_attribute(AVar, Attribute, Pos).
752get_subterm_child(Pos=_FieldName, Current, Top, Child, Module) ?- !,
753% structure with field names
754	get_subterm_child(Pos, Current, Top, Child, Module).
755get_subterm_child(list(Pos), Current0, Top, Child, Module) ?- !,
756	written_term(Top, Current0, Current, Module),
757	list_nth(Pos, Current, Child, _).
758get_subterm_child(tail(Pos), Current0, Top, Tail, Module) ?- !,
759	Pos0 is Pos - 1,
760	written_term(Top, Current0, Current, Module),
761	list_nth(Pos0, Current, _, Tail).
762get_subterm_child(Pos, Current0, Top, Child, Module) :-
763	written_term(Top, Current0, Current, Module),
764	( compound(Current) ->
765              functor(Current, _, A),
766              integer(Pos),
767              A >= Pos, Pos > 0,
768              arg(Pos, Current, Child)
769        ; is_handle(Current), is_expandable_handle(Current, Child) ->
770              true
771	; is_suspension(Current) ->
772	      get_suspension_data(Current, goal, Child), Pos == 1
773	).
774
775
776is_expandable_handle(H, Exp) :-
777        get_event_handler(40, H40, M40),
778        set_event_handler(40, fail/0),
779        get_event_handler(141, H141, M141),
780        set_event_handler(141, fail/0),
781	Reset = (set_event_handler(40,H40)@M40, set_event_handler(141,H141)@M141),
782        catch(
783	    ( xget(H, 0, Exp) ->
784		Reset
785	    ;
786		Reset, fail
787	    ), Tag, (Reset, throw(Tag))
788        ).
789
790list_nth(1, [E0|Ls], E, Tail) ?- !,
791	E = E0, Tail = Ls.
792list_nth(N0, [_|Ls], E, Tail) :-
793	N0 > 1,
794	N1 is N0 - 1,
795	list_nth(N1, Ls, E, Tail).
796
797position_modifier(_Index-AttName, Modifier) ?- !,
798% Is an attribute
799	concat_string([AttName, ':  '], Modifier).
800position_modifier(_ArgPos=FieldName, Modifier) ?- !,
801% structure with named fields
802	concat_string([FieldName, ':  '], Modifier).
803position_modifier(list(N), Modifier) ?- !,
804	(N == 1 ->
805	    Modifier = "[   " ; Modifier = ",   "
806	).
807position_modifier(tail(_), Modifier) ?- !,
808	Modifier = "|   ".
809position_modifier(_, "").
810
811
812inspect_get_children_for_path(SourceS, ChildCommand, PrintDepth,
813						ChildPosList, ChildInfoList) :-
814	ChildCommand = childnodes(_, _, _, PPath),
815	inspect_command(SourceS, ChildCommand, PosList),
816	( foreach(Pos, PosList),
817	  foreach(Child, ChildInfoList),
818	  foreach(CPath, ChildPosList), param(SourceS, PrintDepth, PPath) do
819	    ( string(Pos) ->
820		PosS = Pos
821	    ;
822		term_string(Pos, PosS)
823	    ),
824	    append(PPath, [PosS], CPath),
825	    inspect_command(SourceS, info(PrintDepth, CPath), UnmodifiedChild),
826	    ( integer(Pos) ->
827		Child = UnmodifiedChild
828	    ;
829		inspect_command(SourceS, modify(PosS), Modifier),
830		UnmodifiedChild = [PrintTerm, Summary, Type, Arity],
831		concat_strings(Modifier, PrintTerm, ModifiedPrintTerm),
832		concat_strings(Modifier, Summary, ModifiedSummary),
833		Child = [ModifiedPrintTerm, ModifiedSummary, Type, Arity]
834	    )
835	).
836
837
838%----------------------------------------------------------------------
839% Output mode setting
840%----------------------------------------------------------------------
841
842:- mode get_tracer_output_modes(-).
843get_tracer_output_modes(Modes) :-
844	getval(dbg_goal_format_string, Format),
845	split_string(Format, "G", "%*Gw", ModeList),
846	concat_string(ModeList, Modes).
847
848:- mode set_tracer_output_modes(+).
849set_tracer_output_modes(Modes) :-
850	concat_string(["%*G",Modes,"w"], Format),
851	setval(dbg_goal_format_string, Format).
852
853
854:- mode get_tracer_print_depth(-).
855get_tracer_print_depth(Depth) :-
856	getval(dbg_print_depth, Depth).
857
858
859:- mode set_tracer_print_depth(+).
860set_tracer_print_depth(Depth) :-
861	setval(dbg_print_depth, Depth).
862
863
864%-------------------------------------------------------------------
865%  Grace-like matrix display of variables
866%-------------------------------------------------------------------
867
868%:- open(queue(""), update, matrix_out_queue, [yield(on)]).
869
870/*
871commands sent by Prolog:
872% note Id should always be first arg.
873
874   setup(Id, Name, NRow, NCol, Module)
875       setup display matrix (from Module) with Name and Id, of size NRowxNCol
876
877   displ(Id, Row, Col, String, TermState, BackorForward)
878       String is the printed representation of Term at matrix Id at Row,Col.
879       TermState is the status of the term (for break-points), BackorForward
880       is if this value is from backtracking or forward execution
881
882   kill(Id)
883       Kill the display matrix Id
884
885   interact(Id)
886       interact with user at display matrix Id
887
888Id is used to identify a matrix rather than Name because it is not certain that
889a matrix window will dissapear beyond its `logical scope'. Id is monotonically
890increasing number assigned by the system that ensure each new matrix has a
891unique number
892
893Need to keep two variable containers:
894
895matdisplayid:     the current value of the id. This is incremented whenever
896                  a new matrix is created. Is a variable.
897
898matdisplaydata:   this keeps the actual information associated with all the
899                  matricies. Is a reference.
900*/
901
902:- local struct(matrix(id,name,module,matrix,suspl)).
903
904convert_list_to_matrix(List, 0, Matrix) :- !,
905	Matrix =.. [[]|List].
906convert_list_to_matrix(List, N, Matrix) :-
907	integer(N), N > 0, !,
908	length(List, L),
909	(N >= L ->
910	    Matrix =.. [[]|List]
911	;   divide_list(N, List, LLists, unused, 1, M),
912	    dim(Matrix, [M,N]),
913	    (foreach(L, LLists), foreacharg(Row, Matrix) do
914	         Row =.. [[]|L]
915	    )
916	).
917
918divide_list(N, List0, LLists0, Fill, M0, M) :-
919	make_one_sublist(N, List0, List1, Sub, Fill),
920	(List1 == [] ->
921	    LLists0 = [Sub],
922	    M0 = M
923	;
924	    LLists0 = [Sub|LLists1],
925	    M1 is M0 + 1,
926	    divide_list(N, List1, LLists1, Fill, M1, M)
927	).
928
929make_one_sublist(I, [], List1, Sub, Fill) ?- !,
930	List1 = [],
931	(foreach(E, Sub), count(_, 1, I), param(Fill) do
932            E = Fill
933	).
934make_one_sublist(0, List0, List1, Sub, _) ?- !, List1 = List0, Sub = [].
935make_one_sublist(I0, List0, List, Sub0, Fill) ?-
936	List0 = [E|List1], Sub0 = [E|Sub1],
937	I1 is I0 - 1,
938	make_one_sublist(I1, List1, List, Sub1, Fill).
939
940
941make_display_matrix_body(Matrix, Name, Module) :-
942	make_display_matrix_body(Matrix, 1, any, constrained, Name, Module).
943
944
945/* make_display_matrix_body(+Matrix, +Prio, +Type, +SList, +Name)
946     creates a term display matrix. Matrix is either a list or a matrix of
947     terms. Prio is the priority the demon would be suspended at, Type is
948     what type of information would be displayed, SList is the suspension
949     list the demon's suspension would be added to, and Name is the name
950     used for this display matrix
951*/
952make_display_matrix_body(List/NRow, Prio, Type, SList, Name0, Module) ?- !,
953	convert_list_to_matrix(List, NRow, Matrix),
954	make_display_matrix_body(Matrix, Prio, Type, SList, Name0, Module).
955make_display_matrix_body(List, Prio, Type, SList, Name0, Module) :-
956	List = [_|_], !, % is a list
957	convert_list_to_matrix(List, 0, Matrix),
958	make_display_matrix_body(Matrix, Prio, Type, SList, Name0, Module).
959make_display_matrix_body(Matrix, Prio, Type, SList, Name, Module) :-
960	compound(Matrix), \+(Matrix = _/_),
961	display_matrix_dim(Matrix, 2, Dims),
962	add_matname(Name, Matrix, Module, Id, SL, Module),
963	(set_up_matdisplay(Dims, Matrix, Prio, Type, SList, Name, Id, SL, Module) ->
964	    true ; sepia_kernel:set_bip_error(5)
965	),
966	% kill window on backtracking
967	!, (true ; kill_matdisplay(Name,Module,_), fail).
968make_display_matrix_body(Matrix, Prio, Type, SList, Name, Module) :-
969	sepia_kernel:get_bip_error(Error),
970	(Error == 5 -> kill_matdisplay(Name,Module,_) ; true),
971	error(Error,  make_display_matrix(Matrix, Prio, Type, SList, Name), Module).
972
973/* display_matrix_dim(+Matrix, +MaxDepth, -Dim)
974   returns the dimensions of a display matrix Matrix. MaxDepth is the max.
975   number of dimensions that a matrix will be decomposed to for a
976   display_matrix (cannot be more than 2 as display matrix must be 2d or less)
977*/
978display_matrix_dim(_Matrix, 0, Dim) :- !, Dim = [].
979display_matrix_dim(Matrix, N, [D|Ds]) :-
980	compound(Matrix),
981	functor(Matrix, [], D),
982	N1 is N - 1,
983        (foreacharg(Row, Matrix), param([Ds,N1]) do
984            display_matrix_dim(Row,N1, Ds)
985	), !.
986display_matrix_dim(_Matrix, _, []).
987
988gen_mat_name(Name0, Module, FName) :-
989	(atomic(Name0) ->
990	    concat_string([Name0], NameS),
991	    FName = NameS@Module
992	;   sepia_kernel:set_bip_error(5)
993        ).
994
995set_up_matdisplay([N], Matrix, Prio, Type, SList, Name, Id, SL, Module) ?- !,
996	init_matdisplay(1, N, Name, Id, Module),
997	set_up_matrowdis(1, N, Matrix, Prio, Type, SList, Id, SL, []),
998	matdisplay_interact(Id).
999set_up_matdisplay([M,N], Matrix, Prio, Type, SList, Name, Id, SL, Module) ?-
1000	init_matdisplay(M, N, Name, Id, Module),
1001	(foreacharg(Row, Matrix), param(N,Prio,Type,SList,Id), count(I, 1, M),
1002	 fromto(SL, S0, S1, []) do
1003            set_up_matrowdis(I, N, Row, Prio, Type, SList, Id, S0, S1)
1004	), matdisplay_interact(Id).
1005
1006set_up_matrowdis(CurrentRow, ColSize, Row, Prio, Type, SList, Id, SLIn, SLOut) :-
1007	(foreacharg(E, Row), count(I, 1, ColSize), fromto(SLIn, SL0, SL1, SLOut),
1008	 param(CurrentRow,Prio,Type,Id,SList) do
1009           set_up_mattermdis(E, CurrentRow, I, Prio, Type, SList, Id, SL0, SL1)
1010        ).
1011
1012
1013set_up_mattermdis(E, Row, Col, Prio, Type, SList, Id, [Susp|Out], Out) :-
1014	(nonground(E) ->
1015	    get_display_string(E, Type, String, _),
1016	    suspend(term_display_demon(E,Row,Col,Id,Type,String, Susp), Prio, E->SList, Susp),
1017	    Type1 = Type
1018	;   Type1 = none
1019        ),
1020	send_display_elm(E, Row, Col, Id, Type1, _).
1021
1022matdisplay_interact(Id) :-
1023	write_exdr(matrix_out_queue, interact(Id)),
1024	flush(matrix_out_queue).
1025
1026
1027:- demon term_display_demon/7.
1028:- set_flag(term_display_demon/7, leash, notrace).
1029:- set_flag(term_display_demon/7, skip, on).
1030
1031term_display_demon(Term, Row, Col, Id, Type, _Old, Susp) :-
1032	send_display_elm(Term, Row, Col, Id, Type, String),
1033	(nonground(Term) ->
1034	  get_suspension_data(Susp, goal, Goal),
1035	  setarg(6, Goal, String)
1036        ; kill_suspension(Susp)
1037        ).
1038term_display_demon(Term, Row, Col, Id, _Type, Old, _Susp) :-
1039	(nonground(Term) -> G = nonground ; G = wasground),
1040	send_display_string(Id, Row, Col, Old, G, back),
1041	fail.
1042
1043
1044init_matdisplay(NRow, NCol, Name, Id, Module) :-
1045	write_exdr(matrix_out_queue, setup(Id, Name, NRow, NCol, Module)),
1046	flush(matrix_out_queue).
1047
1048kill_matdisplay(Name, Module, Cond) :-
1049	(shutdown_mat(Name, Id, Module) ->
1050	    Cond = yes,
1051	    write_exdr(matrix_out_queue, kill(Id)),
1052	    flush(matrix_out_queue)
1053	;   Cond = no
1054        ).
1055
1056send_display_elm(E, Row, Col, Id, Type, String) :-
1057	get_display_string(E, Type, String, TState),
1058	send_display_string(Id, Row, Col, String, TState, forward).
1059
1060send_display_string(Id, Row, Col, String, G, State) :-
1061	write_exdr(matrix_out_queue, disp(Id, Row, Col, String, G, State)),
1062	flush(matrix_out_queue).
1063
1064add_matname(Name0,Matrix,Module,Id,SL, Module) :-
1065	getval(matdisplaydata, Mats),
1066	getval(matdisplayid, Id0),
1067	concat_string([Name0], Name),  % make sure it is a string
1068	NewMat = matrix{name:Name,module:Module},
1069	(\+member(NewMat, Mats) -> % \+member because 0 terminates list
1070	    Id is Id0 + 1,
1071	    setval(matdisplayid, Id),
1072	    concat_string([Name0], Name),  % make sure it is a string
1073	    NewMat = matrix{id:Id,matrix:Matrix,suspl:SL},
1074	    setval(matdisplaydata, [NewMat|Mats])
1075	;   sepia_kernel:set_bip_error(6)
1076        ).
1077
1078shutdown_mat(Name0, Id, Module) :-
1079	getval(matdisplaydata, Mats),
1080	concat_string([Name0], Name),
1081	M = matrix{id:Id,name:Name,module:Module,suspl:Ss},
1082	memberchk(M, Mats),
1083        % stop sending of information to GUI side
1084	(foreach(S,Ss) do kill_suspension(S)).
1085
1086get_matrix_term(Id, R, C, Term, Mod) :-
1087	getval(matdisplaydata, Ms),
1088	member(matrix{id:Id,module:Mod,matrix:Mat}, Ms),
1089	dim(Mat, Bounds),
1090	get_subscripts(Bounds, R, C, Sub),
1091	subscript(Mat, Sub, Term).
1092
1093get_subscripts([N], R, C0, [C]) :- !,
1094	(R == 1, N >= C0 ->  C = C0 ;
1095	    writeln(error, "Matrix subscript error"),
1096	    C = N
1097	).
1098get_subscripts([N,M], R0, C0, [R,C]) :- !,
1099	(N >= R0 -> R = R0
1100        ;   writeln(error, "Matrix subscript error"),
1101	    R = N
1102        ),
1103	(M >= C0 -> C = C0
1104        ;   writeln(error, "Matrix subscript error"),
1105            C = M
1106        ).
1107get_subscripts(_, _R, _C, []) :-
1108	writeln(error, "Matrix subscript error").
1109
1110
1111kill_display_matrix_body(Name@Module, _) ?- !,  % for compatibility only
1112	kill_display_matrix_body(Name, Module).
1113kill_display_matrix_body(Name, Module) :-
1114	kill_matdisplay(Name, Module, Cond),
1115	(Cond == yes ->  ! ; sepia_kernel:set_bip_error(6)).
1116kill_display_matrix_body(Name, Module) :-
1117	sepia_kernel:get_bip_error(Error),
1118	error(Error, kill_display_matrix(Name), Module).
1119
1120get_display_string(E, _Type, String, G) :-
1121	(nonground(E) -> G = nonground ; G = ground),
1122	open("", string, S),
1123	printf(S, "%mQPw", [E]),
1124	get_stream_info(S, name, String),
1125	close(S).
1126
1127
1128%---------------------------------------------------------------------
1129% Observed terms
1130%---------------------------------------------------------------------
1131
1132create_observed([], _) :- !.
1133create_observed(Os, trace_line{frame:tf{module:Module}}) :-
1134	make_observed_list(Os, OL),
1135	getval(observed_count, Count),
1136	incval(observed_count),
1137	concat_atom(['Observing#', Count], Label),
1138	make_display_matrix(OL/2, Label)@Module.
1139
1140make_observed_list([], Out) :- !, Out = [].
1141make_observed_list([o(Source,Path,Label)|Os], Out) :-
1142	get_inspected_term(Source, Term, Module),
1143	get_subterm_from_path(Path, Term, Term, Sub, Module),
1144	Out = [Label,Sub|Out1],
1145	make_observed_list(Os, Out1).
1146
1147%----------------------------------------------------------------------
1148% Library browser
1149%----------------------------------------------------------------------
1150
1151init_library_info :-
1152% collect the available libraries with valid info files
1153	collect_library_info(Info),
1154	setval(library_info, Info).
1155
1156collect_library_info(Info) :-
1157	get_flag(library_path, LibPaths),
1158	(foreach(LP, LibPaths), fromto(UnsortedInfo, InfoIn, InfoOut, []) do
1159            get_flag(eclipse_info_suffix, ISuf),
1160	    concat_string(["*", ISuf], IFilter),
1161            read_directory(LP, IFilter, _, Fs0),
1162	    (foreach(File, Fs0), fromto(InfoIn, Info1, Info2,  InfoOut),
1163             param(LP) do
1164		 (get_module_from_infofile(LP, File, MFInfo) ->
1165		      Info1 = [MFInfo|Info2] ; Info1 = Info2
1166		 )
1167	    )
1168	),
1169	sort(module of mfile, <, UnsortedInfo, Info).
1170
1171get_module_from_infofile(Path, File, MFile) :-
1172	concat_string([Path, "/", File], FullName),
1173	get_file_info(FullName, readable, on),
1174	open(FullName, read, In),
1175	(read(In, :-module(Module)) -> % module should be first item in file
1176	    MFile = mfile{dir:Path,module:Module,file:FullName},
1177	    close(In)
1178	;   close(In), fail
1179        ).
1180
1181% assumes File is readable and is a valid information file
1182read_interface_file(File, ILines, CLines) :-
1183	open(File, read, In),
1184	read_interface_file1(In, ILines, CLines),
1185	close(In).
1186
1187read_interface_file1(In, ILines0, CLines0) :-
1188	(at_eof(In)  ->
1189	    ILines0 = [], CLines0 = []
1190	;   (read(In, :-Line) -> % all info in interface file should be directives
1191	        filter_interface_line(Line, ILines0, ILines1, CLines0, CLines1)
1192	    ;
1193		% ignore invalid lines
1194		ILines1 = ILines0,
1195		CLines1 = CLines0
1196	    ),
1197	    read_interface_file1(In, ILines1, CLines1)
1198	).
1199
1200filter_interface_line(module(_), ILines0, ILines, CLines0, CLines) ?- !,
1201	CLines0 = CLines,
1202	ILines0 = ILines.
1203filter_interface_line(comment(T,C), ILines0, ILines, CLines0, CLines) ?- !,
1204	CLines0 = [comment(T,C)|CLines],
1205	ILines0 = ILines.
1206filter_interface_line(Line, ILines0, ILines, CLines, CLines) :-
1207	ILines0 = [Line|ILines].
1208
1209
1210% find the exported predicates, sort them into Preds, and place other info
1211% items into Others in the order they occur in the interface file
1212sort_minfo(MInfo, Preds, Others) :-
1213	(foreach(Item, MInfo), fromto(Preds0, P0,P1, []),
1214	 fromto(Others, O0, O1, []) do
1215             (Item = export(Name/Arity) ->
1216		 P0 = [Name/Arity|P1], O0 = O1
1217	     ;   P0 = P1, O0 = [Item|O1]
1218	     )
1219        ),
1220	sort(Preds0, Preds).
1221
1222% support for returning the children and node content for libbrowser widget
1223
1224extract_lbpath_info(["top"|RestPath], PInfo, Deepest) :-
1225	getval(lbpath_type, [top|PTypes]),
1226	lbindex_info(RestPath, PTypes, top, PInfo, Deepest).
1227
1228lbindex_info([], _PTypes, ParentType, PInfo, Deepest) :-
1229	Deepest = ParentType, PInfo = [].
1230lbindex_info([P|Ps], [Type|Types], _, [PInfo|PInfo0], Deepest) :-
1231	get_onelbindex_info(Type, P, PInfo, ActualType),
1232	lbindex_info(Ps, Types, ActualType, PInfo0, Deepest).
1233
1234get_onelbindex_info(dir, Dir, PInfo, ActualType) ?-
1235	ActualType = dir,
1236	PInfo = dir:Dir.
1237get_onelbindex_info(module, SModule, PInfo, ActualType) ?-
1238	atom_string(Module, SModule), % SModule is a string
1239	is_lbmodule(Module),
1240	ActualType = module,
1241	PInfo = module:Module.
1242get_onelbindex_info(interface, IntPath, PInfo, ActualType) ?-
1243	term_string(Item, IntPath),
1244	(Item = Name/Arity ->
1245	    PInfo = interface:Name/Arity,
1246	    ActualType = interface(predicate)
1247	;   PInfo = interface:Item,
1248	    ActualType = interface(nonpredicate)
1249        ).
1250
1251expand_lbnode(Path, Children) :-
1252	extract_lbpath_info(Path, PInfo, Deepest),
1253	return_lbnode_children(Deepest, PInfo, Children).
1254
1255lbnode_display(Path, DText, Highlight) :-
1256	extract_lbpath_info(Path, PInfo, Deepest),
1257	(Deepest = module ->
1258	    memberchk(module:M, PInfo),
1259	    (get_flag(loaded_library, M) ->
1260		Highlight = current ; Highlight = highlight
1261	    )
1262	;
1263	    Highlight = none
1264	),
1265	return_lbnode_text(Deepest, PInfo, DText).
1266
1267lbnode_info(Path, IsOpen, NodeInfo, Module) :-
1268	extract_lbpath_info(Path, PInfo, Deepest),
1269	return_lbnode_info(Deepest, PInfo, IsOpen, NodeInfo, Module).
1270
1271
1272% load the module Lib
1273lbnode_loadmodule(Lib) :-
1274	get_flag(toplevel_module,Top),
1275	lib(Lib)@Top.
1276
1277% predicates to return information on items
1278
1279return_html_root(Root) :-
1280	get_flag(installation_directory, ECDir),
1281	concat_string([ECDir, "/doc/index.html"], RootInternal),
1282	os_file_name(RootInternal, Root).
1283
1284return_lbnode_children(top, _, Dirs) ?-
1285	return_libdirs(Dirs).
1286return_lbnode_children(dir, PInfo, Modules) ?-
1287	memberchk(dir:Dir, PInfo),
1288	return_modules_in_dir(Dir, Modules).
1289return_lbnode_children(module, PInfo, InterNodes) ?-
1290	memberchk(module:Module, PInfo),
1291	memberchk(dir:Dir, PInfo),
1292	return_module_info(Module, Dir, minfo{interface:Interface}),
1293	sort_minfo(Interface, Preds, Others),
1294	(foreach(P, Preds), fromto(InterNodes, Nodes0, Nodes1, InterNodes1) do
1295              term_string(P, PIndex),
1296              Nodes0 = [PIndex|Nodes1]
1297	),
1298	(foreach(O, Others), fromto(InterNodes1, Nodes0, Nodes1, []) do
1299              term_string(O, OIndex),
1300              Nodes0 = [OIndex|Nodes1]
1301	).
1302return_lbnode_children(interface(predicate), _PInfo, Expanded) ?- !,
1303	% cannot expand predicates yet
1304	Expanded = [].
1305return_lbnode_children(interface(nonpredicate), _PInfo, Expanded) ?-
1306	Expanded = [].
1307
1308
1309return_lbnode_text(top, _, DText) ?- DText = "libraries".
1310return_lbnode_text(dir, PInfo, DText) ?-
1311	DText = Dir,
1312	memberchk(dir:Dir, PInfo).
1313return_lbnode_text(module, PInfo, DText) ?-
1314	memberchk(module:M, PInfo),
1315	memberchk(dir:Dir, PInfo),
1316	(return_module_summary(M, Dir, Summary) ->
1317	    concat_string([M, " \n   ", Summary], DText)
1318        ;   atom_string(M, DText)
1319        ).
1320return_lbnode_text(interface(predicate), PInfo, DText) ?- !,
1321	memberchk(interface:Name/Arity, PInfo),
1322	memberchk(dir:Dir, PInfo),
1323	memberchk(module:M, PInfo),
1324	construct_pred_display(Name, Arity, M, Dir, DText).
1325return_lbnode_text(interface(nonpredicate), PInfo, DText) ?-
1326	memberchk(interface:Item, PInfo),
1327	term_string(Item, DText).
1328
1329construct_pred_display(Name, Arity, M, Dir, DText) :-
1330	return_pred_comment(M, Dir, Name, Arity, PredCom),
1331	(PredCom \== [] ->
1332	    construct_pred_display_from_comments(Name, Arity, PredCom, DText)
1333	;   term_string(Name/Arity, DText)  % no comment info for pred
1334        ).
1335
1336construct_pred_display_from_comments(Name, Arity, PComs, DText) :-
1337	get_pred_summary(PComs, Summary),
1338	construct_pred_template(Name, Arity, PComs, Template),
1339	concat_string([Template, "\n    ", Summary], DText).
1340
1341get_pred_summary(PComs, Sum) :-
1342	(memberchk(summary:Sum, PComs) ; Sum = ""), !.
1343
1344construct_pred_template(Name, Arity, PComs, Template) :-
1345	(memberchk(template:Template,PComs) ->
1346	    true
1347	;
1348	    return_pred_modes(Name, Arity, PComs, Modes),
1349	    generalise_modes(Modes, Mode),
1350	    construct_onepred_template(Name, Arity, PComs, Mode, Template)
1351	).
1352
1353construct_onepred_template(Name, Arity, PComs, Mode, DText) :-
1354	((memberchk(args:ArgDs, PComs), length(ArgDs, Arity)) ->
1355	    (foreach(ArgDesc, ArgDs), foreach(Name, ANames) do
1356	        ((ArgDesc = Name0:_, string(Name0)) ->
1357		    Name = Name0 ; Name = ""
1358		)
1359	    )
1360	;
1361         (count(_,1,Arity), foreach("", ANames) do true)
1362        ),
1363	(foreach(AName, ANames), foreacharg(AMode, Mode), foreach(Arg, ArgsString) do
1364            concat_string([AMode,AName], Arg)
1365	),
1366        construct_pred_template_with_args(Name, Arity, ArgsString, DText).
1367
1368
1369construct_pred_template_with_args(Name, _, Args, DText) :-
1370	(Args == [] ->
1371            concat_string([Name], DText)
1372	;
1373	    DTextList = [Name, "("|ArgList],
1374	    construct_arglist(Args, ArgList),
1375	    concat_string(DTextList, DText)
1376	).
1377
1378construct_arglist([Last], Out) ?- !,
1379	Out = [Last, ")"].
1380construct_arglist([Arg|Args], Out) ?-
1381	Out = [Arg, ", "|Out0],
1382	construct_arglist(Args, Out0).
1383
1384return_pred_modes(Name, Arity, PComs, Modes) :-
1385	findall(Mode, (member(amode:Mode, PComs), functor(Mode, Name, Arity)),
1386           Modes0),
1387	(Modes0 == [] ->
1388	    % no modes found, generate an all '?' mode.
1389	    functor(GenMode, Name, Arity),
1390	    (foreacharg(?,GenMode) do true),
1391	    Modes = [GenMode]
1392	;   Modes0 = Modes
1393        ).
1394
1395return_lbnode_info(top, _, IsOpen, NInfo, M) ?-
1396	M = "",
1397	(IsOpen == 1 ->
1398	    NInfo = [[normal,"ECLiPSe libraries"]] ; NInfo = []
1399	).
1400return_lbnode_info(dir, _PInfo, IsOpen, NInfo, M) ?-
1401	% could add info on purpose of each directory
1402	M = "",
1403	(IsOpen == 1 ->
1404	    NInfo = [[normal,"An ECLiPSe library directory"]] ; NInfo = []
1405	).
1406return_lbnode_info(module, PInfo, IsOpen, NInfo, M) ?-
1407	memberchk(module:M, PInfo),
1408	(IsOpen == 1 ->
1409	    memberchk(dir:Dir, PInfo),
1410	    return_module_desc(M, Dir, MDesc0),
1411	    ( string(MDesc0) ->
1412		NInfo = [[normal, MDesc0]]
1413	    ; MDesc0 = ascii(MDesc) ->
1414	        NInfo = [[normal, MDesc]]
1415	    ; NInfo = [] % don't try to cope with non-plain ascii formats
1416	    )
1417	;
1418            NInfo = []
1419        ).
1420return_lbnode_info(interface(predicate), PInfo, _IsOpen, NInfo, M) ?- !,
1421	memberchk(dir:_Dir, PInfo),
1422	memberchk(module:M, PInfo),
1423	memberchk(interface:Name/Arity, PInfo),
1424	return_pred_info(M, Name, Arity, NInfo).
1425return_lbnode_info(interface(nonpredicate), PInfo, _IsOpen, NInfo, M) ?-
1426	memberchk(module:M, PInfo),
1427	NInfo = [].
1428
1429
1430generalise_modes([Mode|Modes], GenM) :-
1431	generalise_modes(Modes, Mode, GenM).
1432
1433generalise_modes([], Gen, Gen).
1434generalise_modes([Mode1|Modes], Mode2, Gen) :-
1435	functor(Mode1, Name, Arity),
1436	functor(GenMode1, Name, Arity),
1437	(foreacharg(M1, Mode1), foreacharg(M2, Mode2), foreacharg(G, GenMode1) do
1438            lub(M1, M2, G)
1439	),
1440	generalise_modes(Modes, GenMode1, Gen).
1441
1442% lub(PX, PY, PLub)
1443%
1444% least upper bound of 2 modes (cf. lattice above)
1445
1446lub(-, Y, LUB) :- 'lub-'(Y, LUB).
1447lub(++, Y, LUB) :- 'lub++'(Y, LUB).
1448lub(+-, Y, LUB) :- 'lub+-'(Y, LUB).
1449lub(-+, Y, LUB) :- 'lub-+'(Y, LUB).
1450lub(+, Y, LUB) :- 'lub+'(Y, LUB).
1451lub(?, _, ?).
1452
1453'lub-'(-, -).
1454'lub-'(++, -+).
1455'lub-'(+-, -+).
1456'lub-'(-+, -+).
1457'lub-'(+, ?).
1458'lub-'(?, ?).
1459
1460'lub+'(-, ?).
1461'lub+'(++, +).
1462'lub+'(+-, +).
1463'lub+'(-+, ?).
1464'lub+'(+, +).
1465'lub+'(?, ?).
1466
1467'lub++'(-, -+).
1468'lub++'(++, ++).
1469'lub++'(+-, +-).
1470'lub++'(-+, -+).
1471'lub++'(+, +).
1472'lub++'(?, ?).
1473
1474'lub+-'(-, -+).
1475'lub+-'(++, +-).
1476'lub+-'(+-, +-).
1477'lub+-'(-+, -+).
1478'lub+-'(+, +).
1479'lub+-'(?, ?).
1480
1481'lub-+'(-, -+).
1482'lub-+'(++, -+).
1483'lub-+'(+-, -+).
1484'lub-+'(-+, -+).
1485'lub-+'(+, ?).
1486'lub-+'(?, ?).
1487
1488
1489return_module_info(Module, Dir, MInfo) :-
1490	getval(library_info, Info),
1491	memberchk(mfile{module:Module,dir:Dir, file:File}, Info),
1492	read_interface_file(File, InterItems, Comments),
1493	MInfo = minfo{interface:InterItems, comments:Comments}.
1494
1495return_module_summary(Module, Dir, Summary) :-
1496	return_module_info(Module, Dir, minfo{comments:MCom}),
1497	memberchk(comment(summary, Summary), MCom).
1498
1499return_module_desc(Module, Dir, Desc) :-
1500	return_module_info(Module, Dir, minfo{comments:MCom}),
1501	(memberchk(comment(desc, Desc), MCom) ->
1502	    true ; Desc = ""
1503	).
1504
1505return_modules_in_dir(Directory, Modules) :-
1506	getval(library_info, Info),
1507	findall(M, member(mfile{dir:Directory,module:M}, Info), Modules).
1508
1509return_libdirs(Dirs) :-
1510	get_flag(library_path, Dirs).
1511
1512return_pred_comment(M, Dir, Name, Arity, PCom) :-
1513	return_module_info(M, Dir, minfo{comments:MCom}),
1514	(memberchk(comment(Name/Arity, PCom), MCom) ->
1515	    true ; PCom = []
1516	).
1517
1518return_pred_info(M, Name, Arity, PredInfo) :-
1519% just return what help would return
1520	term_string(M:Name/Arity, PredSpecS),
1521	gui_help_string(PredSpecS, Info),
1522	PredInfo = [[normal, Info]].
1523
1524construct_args_descr(PredCom, PredInfoIn, PredInfoOut) :-
1525	(memberchk(args:Args, PredCom) ->
1526	  length(Args, N),
1527	  (N > 0 ->
1528	      open("", string, ArgsDesc),
1529	      (foreach(Name:Desc, Args), param(ArgsDesc) do
1530	          printf(ArgsDesc, "%-20s  %s\n", [Name,Desc])
1531	      ),
1532	      get_stream_info(ArgsDesc, name, ArgsString),
1533	      close(ArgsDesc),
1534	      PredInfoIn = [[heading, "Arguments"],
1535                            [normal, ArgsString],[normal,""]|PredInfoOut]
1536	  ;
1537	      PredInfoIn = PredInfoOut
1538	  )
1539      ;
1540	  PredInfoIn = PredInfoOut
1541      ).
1542
1543% type checks
1544is_lbmodule(Module) :-
1545	atom(Module),
1546	getval(library_info, Info),
1547	memberchk(mfile{module:Module}, Info).
1548
1549
1550%----------------------------------------------------------------------
1551% Handlers for various GUI requests
1552% Most are called from the GUI via RPCs
1553%----------------------------------------------------------------------
1554
1555:- local record(new_source_files).
1556
1557:- open(queue(""), update, gui_dg_buffer).
1558%:- open(queue(""), update, gui_dg_info, [yield(on)]). creation done in gui
1559
1560compile_os_file(OsFile, Module) :-
1561	os_file_name(File, OsFile),
1562	catchall(compile(File, Module)),
1563	% flush here, because the flushes in the nested emulator
1564	% within the compiler are ignored...
1565	flush(warning_output),
1566	flush(error),
1567	flush(output).
1568
1569
1570use_module_os(OsFile, Module) :-
1571	os_file_name(File, OsFile),
1572	catchall(use_module(File)@Module),
1573	% flush here, because the flushes in the nested emulator
1574	% within the compiler are ignored...
1575	flush(warning_output),
1576	flush(error),
1577	flush(output).
1578
1579catchall(Goal) :-
1580        catch(Goal, Tag, top_abort(Tag)).
1581
1582top_abort(abort) ?- !.
1583top_abort(Tag) :-
1584        stack_overflow_message(Tag), !,
1585        top_abort(abort).
1586top_abort(Tag) :-
1587        catch(error(230, throw(Tag)), T, true),
1588        top_abort(T).
1589
1590list_predicates(Which, Module, AuxFilter, Sorted) :-
1591	( Which = exported ->
1592	    Goal = (current_module_predicate(exported_reexported,P)@Module)
1593	; Which = local ->
1594	    Goal = (current_module_predicate(local,P)@Module)
1595	; Which = defined ->
1596	    Goal = (current_module_predicate(defined,P)@Module)
1597	; Which = visible ->
1598	    Goal = (current_predicate(P)@Module;current_built_in(P)@Module)
1599	; Which = imported ->
1600	    Goal = (
1601		(current_predicate(P)@Module;current_built_in(P)@Module),
1602	    	get_flag(P,visibility,V)@Module,
1603		memberchk(V,[imported,reexported])
1604	    )
1605	;
1606	    Goal = fail
1607	),
1608	findall(PS,
1609	    (
1610		not is_locked(Module),
1611		Goal,
1612                filter_auxiliary(AuxFilter, P, Module),
1613		term_string(P,PS)@Module
1614	    ),
1615	    Preds),
1616	sort(0, =<, Preds, Sorted),
1617	true.
1618
1619    filter_auxiliary(1, P, Module) :- get_flag(P,auxiliary,off)@Module.
1620    filter_auxiliary(0, _, _).
1621
1622flag_value(PredS,Name,M,String) :-
1623        % this can be called with an empty selection from Tcl...
1624        PredS \== "",
1625	term_string(Pred, PredS),
1626	get_flag(Pred, Name, Value)@M,
1627	term_string(Value, String).
1628
1629set_flag_string(PredS,Name,Value,M) :-
1630	term_string(Pred, PredS),
1631	set_flag(Pred, Name, Value)@M.
1632
1633record_source_file(XFile) :-
1634	os_file_name(File1, XFile),
1635	canonical_path_name(File1, File2),
1636	atom_string(File3, File2),
1637	( recorded(new_source_files, File3) -> true
1638	; record(new_source_files, File3) ).
1639
1640get_source_info(PredS, M, OSFile, Offset) :-
1641        term_string(N/A, PredS),
1642        atom(N),
1643        integer(A),
1644        current_module(M), % may fail
1645        % source_line and source_offset are for end of last predicate
1646        get_flag(N/A, source_file, File)@M,
1647        get_flag(N/A, source_offset, Offset)@M,
1648        os_file_name(File, OSFile).
1649
1650
1651% list_files/1 returns a list of lists of strings of the form
1652% ["filename", "status", "module"] where the filename is in the
1653% syntax of the operating system
1654
1655list_files(Files) :-
1656	findall([F,S,M], source_file_status(F,S,M), Files).
1657
1658    source_file_status(XFile, State, SModule) :-
1659	current_compiled_file(File,CompileTime,Module),
1660	( erase(new_source_files, File),fail ; true ),
1661	( get_file_info(File,mtime) =:= CompileTime ->
1662	    State = "ok"
1663	; get_file_info(File,mtime,_) ->
1664	    State = "modified"
1665	;
1666	    State = "nonexisting"
1667	),
1668	atom_string(Module, SModule),
1669	atom_string(File, SFile),
1670	os_file_name(SFile, XFile).
1671    source_file_status(XFile, "new", "") :-
1672	recorded(new_source_files, File),
1673	atom_string(File, SFile),
1674	os_file_name(SFile, XFile).
1675
1676
1677list_modules(Modules, ToplevelModule) :-
1678	findall(Module, current_module(Module), Modules),
1679        get_flag(toplevel_module, ToplevelModule).
1680
1681% gui_help(+Stream, +Subject)
1682% prints help on the string Subject onto Stream
1683
1684gui_help(Stream,SubjectString) :-
1685	get_stream(output, S),
1686	set_stream(output, Stream),
1687	( catch(gui_help1(SubjectString), _, fail) ->
1688	    true
1689	;
1690	    printf("No help available on \"%s\"%n", SubjectString)
1691	),
1692	set_stream(output, S),
1693	flush(Stream).
1694
1695    gui_help1(SubjectString) :-
1696    	term_string(Subject,SubjectString),	% for name/arity terms
1697	( var(Subject) ->
1698	    help(SubjectString)			% for upper case words
1699	;
1700	    help(Subject)
1701	).
1702
1703% returns the help info as a string (Info), given the pred. spec. as a string
1704gui_help_string(PredSpecS, Info) :-
1705	open(string(""), write, s),
1706	gui_help(s, PredSpecS),
1707	get_stream_info(s, name, Info),
1708	close(s).
1709
1710
1711% gui_dg(+Which,+Trigger,+Filter)
1712% send delayed goals to gui, filtering out goals according to filter
1713% Which specifies if the goals should be obtained from the global
1714% list (0), or from the symbolic trigger Trigger (1)
1715% currently Filter is: dg_filter(tracedonly, spiedonly,scheduledonly)
1716% tracedonly: only send traced goals
1717% speidonly: only send spied goals
1718% scheduledonly: only send scheduled goals
1719
1720gui_dg(Which, Trigger, Filter) :-
1721	get_suspensions(Which, Trigger, Susps),
1722	( foreach(Susp, Susps), param(Filter) do
1723	    ( suspension_info(Susp,Filter) -> true ; true ),
1724	    % flush before the queue buffers get too large...
1725	    ( at(gui_dg_info) > 32000 ->
1726		write_exdr(gui_dg_info, end),
1727	    	flush(gui_dg_info)
1728	    ; true )
1729	),
1730	write_exdr(gui_dg_info, end),
1731	flush(gui_dg_info).
1732
1733    get_suspensions(0, _, Susps) :-   % all suspensions
1734        suspensions(Susps).
1735    get_suspensions(1, Trigger, Susps) :-
1736	( is_list(Trigger) ->
1737	    ( foreach(T, Trigger), fromto(Susps, ThisNext, Next, [])
1738	    do
1739		attached_suspensions(T, This),
1740		append(This, Next, ThisNext)
1741	    )
1742	;
1743            attached_suspensions(Trigger, Susps)
1744	).
1745
1746    suspension_info(S, Filter) :-
1747	get_suspension_data(S, goal, Goal),
1748	get_suspension_data(S, module, M),
1749	get_suspension_data(S, invoc, Invoc),
1750	get_suspension_data(S, priority, Prio),
1751	get_suspension_data(S, state, State),
1752	( get_suspension_data(S, spy, on) -> Spied = 0'+ ; Spied = 0'  ),
1753	functor(Goal, F, A),
1754	filter_dg(F/A, Filter, dg_filter{spiedonly:Spied,wakeonly:State}),
1755	printf(gui_dg_buffer, "%n %c(%d) <%d>  ", [Spied, Invoc, Prio]),
1756        % delay goals are printed with format and depth options of tracer
1757	getval(dbg_goal_format_string, Format),
1758	getval(dbg_print_depth, PDepth),
1759	printf(gui_dg_buffer, Format, [PDepth,Goal])@M,
1760	read_string(gui_dg_buffer, end_of_file, LineLength, DGString),
1761	write_exdr(gui_dg_info, info(State,Prio,Invoc,LineLength,DGString)).
1762
1763   % filter_dg fails if the Suspended goal is not to be sent to the gui side
1764   % filter_dg(+PredSpec, +FilterSettings, +FilterValues)
1765   % FilterSettings is the settings for the various filters from the gui
1766   % and FilterValues is any value of the delay goal that may be relevant in
1767   % determining if a particular filter should be applied. Both are in the
1768   % dg_filter structure. PredSpec is the predicate spec. for the delayed goal
1769
1770   filter_dg(F/A, Filter, Values) :-
1771	state_filter(Filter, Values),
1772	spied_filter(Filter, Values),
1773	traced_filter(Filter, F/A).
1774
1775      spied_filter(dg_filter{spiedonly:1}, dg_filter{spiedonly:Spied}) ?- !,
1776	  Spied == 0'+.
1777      spied_filter(_, _).
1778
1779      state_filter(dg_filter{wakeonly:1}, dg_filter{wakeonly:State}) ?- !,
1780	  State == 1.
1781      state_filter(_, _).
1782
1783      traced_filter(dg_filter{traceonly: 1}, F/A) ?-
1784	  get_flag(F/A, leash, notrace), !,
1785	  fail.
1786      traced_filter(_, _).
1787
1788get_triggers(Ts) :-
1789        findall(T, current_trigger(T), Ts).
1790
1791get_goal_info_by_invoc(Invoc, Spec, TSpec, Module, LookupModule, Path, From, To) :-
1792% TSpec is write transformed goal spec.
1793	find_goal_by_invoc(Invoc, LookupModule, Goal0, Module0, Path0, _Linum, From, To),
1794        (Path0 == '' ->
1795            Path = no
1796        ;
1797            os_file_name(Path0, OSPath),
1798            Path = p(OSPath)
1799        ),
1800        check_at_wrapper(Goal0, Module0, Goal, Module),
1801	getval(dbg_goal_format_string, Mode),
1802	perform_transformation(Goal, Goal, Mode, TGoal, Module),
1803	functor(Goal, F,A), !,
1804	functor(TGoal, TF, TA),
1805	term_string(F/A,Spec),
1806	term_string(TF/TA,TSpec).
1807get_goal_info_by_invoc(_, "unknown", "unknown", "unknown", "unknown","","","").
1808
1809% this catches any Goal@M calls and returns Goal and M as the goal and module
1810check_at_wrapper(Goal0@M0, _, Goal, M) ?- !,
1811	Goal = Goal0, M = M0.
1812check_at_wrapper(Goal, M, Goal, M).
1813
1814
1815compile_string(String) :-
1816	get_flag(toplevel_module, M),
1817	open(String, string, S),
1818	compile_stream(S)@M,
1819	close(S),
1820	flush(warning_output), % for warnings
1821	flush(error),	       % for errors
1822	flush(output).	       % for compiled-messages
1823
1824
1825find_goal_by_invoc(Invoc, DefModule, Goal, Module, Path, Linum, From, To) :-
1826	getval(exec_state, Current),
1827	Current = trace_line{frame:Stack},
1828	find_goal(Invoc, Stack, Frame),
1829	Frame = tf{goal:Goal, path:Path, line:Linum, from:From, to:To, module:Module},
1830	get_tf_prop(Frame, module, DefModule).
1831
1832get_ancestors(Anc) :-
1833	getval(exec_state, trace_line{frame:Frame}),
1834	(Frame = tf{parent:Stack} ->
1835	    get_ancestors_info(Stack, [], Anc)
1836	    % Anc are returned with oldest first; printing in GUI can then
1837	    % be from top to bottom
1838	;   Anc = [] % no ancestors, 0'th goal
1839        ).
1840
1841get_ancestors_info(Frame0, Anc0, Anc) :-
1842	(Frame0 == 0 ->  % only at depth 0
1843	     Anc0 = Anc
1844
1845	 ;   open(string(""), write, SS),
1846	     make_trace_line(SS, trace_line{port:'....',frame:Frame0},
1847                             Depth, _Port, Invoc, Prio, _Path, _Linum, _From, _To),
1848 	     get_stream_info(SS, name, Line),
1849	     close(SS),
1850	     Frame0 = tf{parent: PFrame},
1851	     get_ancestors_info(PFrame, [a(Depth,Invoc,Prio,Line)|Anc0], Anc)
1852	 ).
1853
1854get_current_traceline(Depth, Style, Line, Invoc) :-
1855        getval(exec_state, Current),
1856        open(string(""), write, SS),
1857	make_trace_line(SS, Current, Depth, Port, Invoc, _Prio, _Path, _Linum, _From, _To),
1858	get_stream_info(SS, name, Line),
1859	close(SS),
1860	port_style(Port, Style).
1861
1862is_current_goal(Invoc, Style) :-
1863        getval(exec_state, trace_line{frame:Frame,port:Port}),
1864        Frame = tf{invoc:Invoc},
1865        port_style(Port, Style).
1866
1867%-------------------------------------------------------------------
1868% statistics reporting
1869%-------------------------------------------------------------------
1870
1871%:- open(queue(""), update, statistics_out_queue, [yield(on)]).
1872
1873report_stats(Int, Stats) :-
1874	get_memory(Stats, Stats1),
1875	get_times(Stats1),
1876	set_event_handler(stat_report, reporting/0),
1877	event_after_every(stat_report, Int).
1878
1879change_report_interval(New) :-
1880	cancel_after_event(stat_report, _),
1881	event_after_every(stat_report, New).
1882
1883stop_report_stats :-
1884	cancel_after_event(stat_report, _).
1885
1886reporting :-
1887        \+ \+ gen_and_send_stats. % recover memory used when generating stats
1888
1889gen_and_send_stats :-
1890	get_memory(Stats, Stats1),
1891	get_times(Stats1),
1892	write_exdr(statistics_out_queue, Stats),
1893	flush(statistics_out_queue).
1894
1895get_memory(Stats, Tail) :-
1896	get_flag(max_global_trail, MaxGT),
1897	statistics(global_stack_allocated, GAlloc),
1898	statistics(global_stack_used, GUsed),
1899	statistics(global_stack_peak, GPeak),
1900	statistics(trail_stack_allocated, TAlloc),
1901	statistics(trail_stack_used, TUsed),
1902	statistics(trail_stack_peak, TPeak),
1903	get_flag(max_local_control, MaxLC),
1904	statistics(local_stack_allocated, LAlloc),
1905	statistics(local_stack_used, LUsed),
1906	statistics(local_stack_peak, LPeak),
1907	statistics(control_stack_allocated, CAlloc),
1908	statistics(control_stack_used, CUsed),
1909	statistics(control_stack_peak, CPeak),
1910	statistics(shared_heap_allocated, SHAlloc),
1911	statistics(shared_heap_used, SHUsed),
1912%	statistics(private_heap_allocated, PHAlloc),
1913%	statistics(private_heap_used, PHUsed),
1914	Stats = [[memory, "global and trail stacks", MaxGT, "maximum size of global/trail stacks", stack(global, GAlloc, GUsed, GPeak), stack(trail, TAlloc, TUsed, TPeak)],
1915           [memory, "local and control stacks", MaxLC, "maximum size of local/control stacks", stack(local, LAlloc, LUsed, LPeak), stack(control, CAlloc, CUsed, CPeak)],
1916%	   [memory, "shared and private heap", SHAlloc, "currently allocated size of shared heap", stack(shared, SHAlloc,SHUsed), stack(private, PHAlloc, PHUsed)]
1917	   [memory, "shared heap", SHAlloc, "currently allocated size of shared heap", stack(shared, SHAlloc,SHUsed)]
1918	   |Tail].
1919
1920get_times(Stats) :-
1921	statistics(times, [User, _System, Real]),
1922	statistics(gc_time, GCTime),
1923	statistics(gc_number, NGC),
1924	statistics(gc_collected, Collected),
1925	statistics(gc_ratio, GCRatio),
1926	Stats = [[times, User, Real, gc(GCTime, NGC, Collected, GCRatio)]].
1927
1928start_report_stats_text_summary(Int) :-
1929        report_stats_text_summary,
1930	stop_report_stats_text_summary,
1931	set_event_handler(stat_report_text_summary, report_stats_text_summary/0),
1932	event_after_every(stat_report_text_summary, Int).
1933
1934stop_report_stats_text_summary :-
1935	cancel_after_event(stat_report_text_summary, _).
1936
1937report_stats_text_summary :-
1938        \+ \+ statistics(statistics_text_summary_queue).
1939
1940
1941%----------------------------------------------------------------------
1942% source debugging/breakpoints
1943%----------------------------------------------------------------------
1944
1945file_is_readable(OSFile) :-
1946        os_file_name(File, OSFile),
1947        get_file_info(File, readable, on). % may fail
1948
1949read_file_for_gui(OSFile) :-
1950        os_file_name(File, OSFile),
1951        get_file_info(File, readable, on), % may fail
1952        open(File, read, S),
1953        repeat,
1954        ( read_string(S, end_of_file, 32000, Part) ->
1955            write_exdr(gui_source_file, Part),
1956            flush(gui_source_file),
1957            fail
1958        ;
1959            !
1960        ),
1961        write_exdr(gui_source_file, ""),
1962        flush(gui_source_file),
1963        close(S).
1964
1965toggle_source_breakpoint(OSFile, Line, PortLine, From, To) :-
1966        os_file_name(File, OSFile),
1967        find_matching_breakport(File, Line, FullName, DMs, PortPreds, PortLine),
1968        ( foreach(PortPred, PortPreds), foreach(DM, DMs),
1969          param(FullName, PortLine, From, To)
1970        do
1971            get_flag(PortPred, break_lines, PInfo)@DM,
1972            ( portline_state(FullName, PortLine, PInfo, From) ->
1973                (From == on -> To = off ; To = on),
1974                set_proc_flags(PortPred, break, PortLine, DM)
1975            ;
1976                % don't toggle if there is a difference in break status
1977                true
1978            )
1979        ).
1980
1981    portline_state(File,Line, PInfo, From) :-
1982	store_create(Cache),
1983        (member(File0:Line, PInfo), cached_canonical_path_name(File0,File,Cache) ->
1984            From = on
1985        ;
1986            From = off
1987        ).
1988
1989breakpoints_for_file(OSFile, BreakLines, PortLines, Preds) :-
1990        os_file_name(File, OSFile),
1991        get_portlist_from_file(File, port_lines, _, Ports),
1992        get_portlist_from_file(File, break_lines, _, Breaks),
1993        ( foreach(port(PL-_,PredSpec), Ports),
1994          foreach(PL, PortLines), foreach((PredString,PL), Preds0)
1995        do
1996            term_string(PredSpec, PredString)
1997        ),
1998        ( foreach(port(BL-_,_), Breaks), foreach(BL, BreakLines) do true),
1999        sort(1, <, Preds0, Preds).
2000
2001find_matching_callinfo(OSFile, Line, PortPredS, CallSpec) :-
2002        os_file_name(File, OSFile),
2003        % ignore problem with possible multiple modules for the same file
2004        find_matching_breakport(File, Line, FullName, [DM|_], [PortPred|_], PortLine),
2005        get_flag(PortPred, port_lines, LInfos)@DM,
2006        get_flag(PortPred, port_calls, CInfos)@DM,
2007        term_string(PortPred, PortPredS),
2008        get_callinfo(FullName:PortLine, LInfos, CInfos, CallSpec).
2009
2010find_exact_callinfo(OSFile, Line, CallSpec) :-
2011        % OSFile must be an atom
2012        os_file_name(File0, OSFile),
2013	store_create(Cache),
2014        cached_canonical_path_name(File0, File, Cache),
2015        current_predicate_with_port(port_lines, PredSpec, Module, File1:Line),
2016        cached_canonical_path_name(File1, File, Cache),
2017        !,
2018        get_flag(PredSpec, port_lines, LInfos)@Module,
2019        get_flag(PredSpec, port_calls, CInfos)@Module,
2020        get_callinfo(File:Line, LInfos, CInfos, CallSpec).
2021
2022get_callinfo(File:Line, [File0:Line|_], [CallSpec|_], CallSpec) :-
2023        canonical_path_name(File0, File), !.
2024get_callinfo(PredPos, [_|PInfos], [_|CInfos], CallSpec) :-
2025        get_callinfo(PredPos, PInfos, CInfos, CallSpec).
2026
2027% On Windows, canonical_path_name can be really slow!
2028cached_canonical_path_name(Path, CanPath, Cache) :-
2029	( store_get(Cache, Path, CanPath0) ->
2030	    true
2031	;
2032	    canonical_path_name(Path, CanPath0),
2033	    store_set(Cache, Path, CanPath0)
2034	),
2035	CanPath0 = CanPath.
2036
2037%----------------------------------------------------------------------
2038% Initialise toplevel module
2039%----------------------------------------------------------------------
2040init_toplevel_module :-
2041	get_flag(toplevel_module,  Top),
2042	erase_module(Top),
2043	create_module(Top, [], eclipse_language).
2044
2045%----------------------------------------------------------------------
2046% Installation - the part that redefines existing toplevel interface
2047% Must be called before gui tools can be used
2048%----------------------------------------------------------------------
2049
2050install_guitools :-
2051% if trace_line_handler_tcl already set, don't do it again
2052	(get_event_handler(252,trace_line_handler_tcl/2,tracer_tcl) ->
2053	    true
2054	;
2055% openning of these queues now done at attachment
2056%	    open(queue(""), update, debug_output, [yield(on)]),
2057%	    open(queue(""), update, debug_traceline, [yield(on)]),
2058	    set_default_error_handler(250, trace_start_handler_tcl/0),
2059	    reset_event_handler(250),
2060	    set_default_error_handler(252, trace_line_handler_tcl/2),
2061	    reset_event_handler(252),
2062
2063	    get_flag(hostarch, Arch),
2064            ( (Arch == "i386_nt" ; Arch == "x86_64_nt") ->
2065		true
2066	    ;
2067		% Catch fatal signals - this is mainly intended for tkeclipse,
2068		% to stop the window from disappearing on such a signal.
2069		% It is important that these are asynchronous handlers which,
2070		% on Unix, execute on their own sigstack. Otherwise they
2071		% wouldn't work on C stack overflow.
2072		( current_interrupt(_, segv) ->
2073		    set_interrupt_handler(segv, catch_fatal/0) ; true ),
2074		( current_interrupt(_, bus) ->
2075		    set_interrupt_handler(bus, catch_fatal/0) ; true )
2076	    )
2077	).
2078
2079%----------------------------------------------------------------------
2080% Uninstallation - undo the installation of guitools' event handlers
2081% Does not try to install another debugger
2082%----------------------------------------------------------------------
2083
2084uninstall_guitools :-
2085	set_default_error_handler(250, true/0),
2086	reset_event_handler(250),
2087	set_default_error_handler(252, true/0),
2088	reset_event_handler(252).
2089
2090%----------------------------------------------------------------------
2091% Interrupt handlers
2092%----------------------------------------------------------------------
2093
2094catch_fatal :-
2095	throw(fatal_signal_caught).
2096
2097%----------------------------------------------------------------------
2098% Saros Filename involved predicates
2099%----------------------------------------------------------------------
2100
2101saros_get_library_path(OSDirs) :-
2102	get_flag(library_path,Dirs),
2103	( foreach(Dir, Dirs), foreach(OSDir, OSDirs) do
2104	    os_file_name(Dir, OSDir)
2105	).
2106
2107saros_set_library_path(OSDirs) :-
2108	( foreach(OSDir, OSDirs), foreach(Dir, Dirs) do
2109	    os_file_name(Dir, OSDir)
2110	),
2111	set_flag(library_path,Dirs).
2112
2113saros_compile(OSFile) :-
2114	os_file_name(File, OSFile),
2115	compile(File).
2116
2117saros_fcompile(OSFile, OSOutDir) :-
2118	get_flag(toplevel_module, Module),
2119	os_file_name(File, OSFile),
2120	os_file_name(OutDir, OSOutDir),
2121	Options = [compile:no, outdir:OutDir],
2122	fcompile:fcompile(File, Options)@Module.
2123
2124saros_icompile(OSFile, OSOutDir) :-
2125	get_flag(toplevel_module, Module),
2126	os_file_name(File, OSFile),
2127	os_file_name(OutDir, OSOutDir),
2128	document:icompile(File, OutDir)@Module.
2129
2130saros_eci_to_html(OSFile, OSHtmlTopDir, Header) :-
2131	os_file_name(File, OSFile),
2132	os_file_name(HtmlTopDir, OSHtmlTopDir),
2133	document:eci_to_html(File, HtmlTopDir, Header).
2134
2135saros_ecis_to_htmls(OSDirs, OSHtmlTopDir, LinkBack, SystemName) :-
2136	( is_list(OSDirs) ->
2137	    ( foreach(OSDir, OSDirs), foreach(Dir, Dirs) do
2138		os_file_name(Dir, OSDir)
2139	    )
2140	;
2141		os_file_name(Dirs, OSDirs)
2142	),
2143	os_file_name(HtmlTopDir, OSHtmlTopDir),
2144	document:ecis_to_htmls(Dirs, HtmlTopDir, LinkBack, SystemName).
2145
2146saros_cd(OSDir) :-
2147	os_file_name(Dir, OSDir),
2148	cd(Dir).
2149
2150saros_use_module(OSFile) :-
2151	os_file_name(File, OSFile),
2152	use_module(File).
2153
2154saros_get_goal_info_by_invoc(Invoc, UseLookupModule, Spec, TSpec,
2155                             Module, LookupModule, Path, From, To, Spied) :-
2156	get_goal_info_by_invoc(Invoc, Spec, TSpec,
2157                               Module, LookupModule, Path, From, To),
2158	( LookupModule == "unknown" ->
2159	    Spied = "off"
2160	;
2161	    ( UseLookupModule = 1 ->
2162		flag_value(Spec, spy, LookupModule, Spied)
2163	    ;
2164		flag_value(Spec, spy, Module, Spied)
2165	    )
2166	).
2167
2168
2169
2170