
:- ensure_loaded(library(debug)).
% :- debug(shell).

%%  run the shell command; ignore output
shell_format(Format, Args) :-
	/*
	  This is greatly complicated by SWIPL not handling
	  concurrent shell calls properly; so we do the
	  shell call in the background, quickly, with a mutex,
	  then wait for a flag file.

	  We could use a FIFO or something too, but checking
	  for a file takes a trivial 1.7 usec on my system.
	*/
	sformat(String, Format, Args),
	string_to_atom(String, Atom),
	debug(shell, 'original shell cmd: ~q', [Atom]),
	tmp_file(shell, FlagFile),
	concat_atom(['(', Atom, '; >', FlagFile, ') &'], Atom2),
	debug(shell, 'mangled  shell cmd: ~q', [Atom2]),
	with_mutex(shell_mutex, shell(Atom2)),
	wait_for(FlagFile),
	delete_file(FlagFile),
	!.

%%  run the shell command; read output into Codes array
shell_format_codes(Format, Args, Codes) :-
	sformat(String, Format, Args),
	string_to_atom(String, Atom),
	debug(shell, 'shell: ~q', [Atom]),
	with_mutex(shell_mutex, open(pipe(Atom), read, Stream, [])),
	read_stream_to_codes(Stream, Codes),
	close(Stream).

shell_format_terms(Format, Args, Terms) :-
	debug(shell, 'shell_format_terms called', []),
	sformat(String, Format, Args),
	string_to_atom(String, Atom),
	debug(shell, 'shell: ~q', [Atom]),
	with_mutex(shell_mutex, open(pipe(Atom), read, Stream, [])),
	debug(shell, 'shell pipe opened', []),
	read_stream_to_terms(Stream, Terms),
	close(Stream),
	debug(shell, 'shell pipe closed', []).

read_stream_to_terms(Stream, Terms) :-
	read_term(Stream, Term, []),
	(   Term = end_of_file
	->  Terms = []
	;   Terms = [Term | More],
	    read_stream_to_terms(Stream, More)
	).

shell_format_term(Format, Args, Term) :-
	shell_format_codes(Format, Args, Codes),
	atom_codes(Atom, Codes),
	term_to_atom(Term, Atom).

wait_for(File) :-
	repeat,
	(
	  sleep(0.03),  % experimentally, shorter periods dont help.
	  exists_file(File)
	).

/*
  DOESNT PASS TESTS RUNNING IN MULTI-THREADED ENVIRONMENT.  DONT USE.
  
:- use_module(library(unix)).

% acts just like call/1 except that the call is done on a fork, so
% it cannot have side-effects on us.   It can succeed (with unification
% of arguments, fail, or throw something.
%
% DOESNT WORK in non-main thread, because they can't call "halt".
%
fork_call(Goal) :-
	pipe(Read, Write),
        fork(Pid),
        (   Pid == child

        ->  close(Read),
	    block(child,
		  (   catch(Goal,
			    Error,
			    exit(child, error(Error)))
		  ->  exit(child, true(Goal))
		  ;   exit(child, failed)
		  ),
		  Exit),
	    format(Write, '~q.~n', [Exit]),
            flush_output(Write),
            halt

        ;   close(Write),
            read(Read, Result),
	    close(Read),
	    (   Result=error(Error)
	    ->  throw(Error)
 	    ;   Result=true(Goal)
	    )
        ).

unit_test(( fork_call(X is 7-4), X=3 )).
unit_test(( fork_call(truePID(CHILDPID)), prolog_flag(pid, PID), PID \= CHILDPID )).

*/

%  prolog_flag(pid, ...) is not changed by fork/1.
truePID(P) :-
	read_link('/proc/self', PidAtom, _),
	atom_number(PidAtom, P).

unit_test(( truePID(PID), prolog_flag(pid, PID) )).

