% synchronous process support

%_debug_info = 1;
private variable Filter_Processes = NULL;

private define find_filter_process (pid)
{
   variable s = Filter_Processes;
   while (s != NULL)
     {
	if (s.pid == pid)
	  break;
	s = s.next;
     }
   
   return s;
}

private define delete_filter_process (s)
{
   if (Filter_Processes == NULL)
     return;

   if (Filter_Processes == s)
     {
	Filter_Processes = s.next;
	s.next = NULL;
	return;
     }

   variable prev = Filter_Processes;
   variable next = prev.next;
	
   while (next != s)
     {
	if (next == NULL)
	  return;
	
	prev = next;
	next = next.next;
     }
   
   prev.next = next.next;
   s.next = NULL;
}

private define process_signal_handler (pid, flags, status)
{
   if (flags == 1)
     return;

   variable s = find_filter_process (pid);
   if (s == NULL)
     return;

   if (flags == 4)
     s.exit_status = status;
   else
     s.exit_status = -1;
}

   
private define allocate_filter_process (pid)
{
   variable s = struct
     {
	pid, exit_status,
	  next
     };
   
   s.pid = pid;
   s.next = Filter_Processes;
   Filter_Processes = s;
   return s;
}

	  
	  
%!%+
%\function{open_filter_process}
%\synopsis{Open a subprocess as a filter}
%\usage{Int_Type pid = open_filter_process (String_Type argv[], String_Type output)}
%\description
% The \var{open_filter_process} function may be used to open an interactive
% synchronous process.  The first argument should be an array of strings
% representing the program to be run in the subprocess, and the command line
% parameters passed to it.  The second argument specifies what to do with the
% output generated by the process.  It can be any value supported by the 
% "output" option of the \var{set_process} function.  The process should be 
% closed using the \var{close_filter_process} function.
%\seealso{close_filter_process, send_process, call_process_region}
%!%-
public define open_filter_process (argv, output)
{
   if (typeof (argv) != Array_Type)
     argv = [argv];

   variable nargs = length (argv);
   foreach (argv)
     ;
   variable args = __pop_args (nargs);
   
   variable pgm = argv[0];
   
   variable pid = open_process_pipe (__push_args (args), nargs-1);
   if (-1 == pid)
     verror ("failed to execute %s", pgm);
   
   variable s = allocate_filter_process (pid);

   set_process (pid, "signal", &process_signal_handler);
   set_process (pid, "output", output);
   
   return pid;
}

%!%+
%\function{close_filter_process}
%\synopsis{Close a filter process and return its status}
%\usage{Int_Type close_filter_process (Int_Type pid)}
%\description
% The \var{close_filter_process} function waits for the specified process
% to terminate and returns the exit status of the process.  The process must
% have been previously opened via the \var{open_filter_process} function.
%\seealso{open_filter_process, send_process, get_process_input}
%!%-
public define close_filter_process (pid)
{
   variable s = find_filter_process (pid);
   if (s == NULL)
     return -1;

   %send_process_eof (pid);
   get_process_input (0);

   variable next_signal = 2;
   while (s.exit_status == NULL)
     {
	update (0);
	ERROR_BLOCK
	  {
	     signal_process (pid, next_signal);
	     if (next_signal != 9)
	       _clear_error ();
	     next_signal = 9;
	  }
	get_process_input (5);
     }
   
   variable status = s.exit_status;
   delete_filter_process (s);
   return status;
}

public define call_process_region (cmd, output)
{
   variable str = bufsubstr ();
   variable pid = open_filter_process (cmd, output);
   if (pid == -1)
     return;
   
   send_process (pid, str);
   send_process_eof (pid);
   vmessage ("Process returned %d", close_filter_process (pid));
}

