 /*
  * Khoros: $Id: utils.c,v 1.2 1992/03/20 22:44:49 dkhoros Exp $
  */

#if !defined(lint) && !defined(SABER)
static char rcsid[] = "Khoros: $Id: utils.c,v 1.2 1992/03/20 22:44:49 dkhoros Exp $";
#endif

 /*
  * $Log: utils.c,v $
 * Revision 1.2  1992/03/20  22:44:49  dkhoros
 * VirtualPatch5
 *
  */ 

/*
 *----------------------------------------------------------------------
 *
 * Copyright 1990, University of New Mexico.  All rights reserved.
 * Permission to copy and modify this software and its documen-
 * tation only for internal use in your organization is hereby
 * granted, provided that this notice is retained thereon and
 * on all copies.  UNM makes no representations as too the sui-
 * tability and operability of this software for any purpose.
 * It is provided "as is" without express or implied warranty.
 * 
 * UNM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FIT-
 * NESS.  IN NO EVENT SHALL UNM BE LIABLE FOR ANY SPECIAL,
 * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY OTHER DAMAGES WHAT-
 * SOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PER-
 * FORMANCE OF THIS SOFTWARE.
 * 
 * No other rights, including for example, the right to redis-
 * tribute this software and its documentation or the right to
 * prepare derivative works, are granted unless specifically
 * provided in a separate license agreement.
 *---------------------------------------------------------------------
 */

#include "unmcopyright.h"        /* Copyright 1990 by UNM */
#include "concert.h"
#include <errno.h>


/* >>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
   >>>>                                                       <<<<
   >>>>         	Colaborator Utilities		      <<<<
   >>>>			   start_command		      <<<<
   >>>>			   add_display			      <<<<
   >>>>			   delete_display		      <<<<
   >>>>                                                       <<<<
   >>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */


/************************************************************
*
*  Module Name: start_command
*
*      Purpose: This routine is used to start the command for
*		the different displays.
*
*        Input: command - command to be executed
*		master  - whether to start as master/slave
*		display - the display to start the command for
*
*       Output: none
*
*   Written By: Mark Young
*
*************************************************************/

start_command(command, master, display)

char *command, *display;
int  master;
{
	char	temp[MaxLength];
	int	ipipe[2], opipe[2],
		create_input  = False,
		create_output = False;
	void	command_callback();
	DisplayStructure *entry, *add_display();


	if (display != NULL)
	{
	   (void) sprintf(temp,"DISPLAY=%s",display);
	   vputenv(xvf_strcpy(temp));
	}

	if (master)
	{
	   if (display != NULL)
	   {
	      create_input = True;
	      sprintf(temp,"%s -jg 0 -jp -", command);
	   }
	   else
	   {
	      create_output = True;
	      sprintf(temp,"%s -jg 0 -jr -", command);
	   }
	}
	else
	{
	   create_input = create_output = True;
	   sprintf(temp,"%s -jg 0 -jp - -jr -", command);
	}

	/*
	 *  Add a callback for the input file, enabling us to propagate
	 *  the data to other displays.
	 */
	entry = add_display(display);
	if (create_input)
	{
	   pipe(ipipe);
	   entry->input  = ipipe[1];
	}
	else
	   entry->input = -1;

	if (create_output)
	{
	   pipe(opipe);
	   entry->output = opipe[0];
	   entry->id = XtAppAddInput(xvf_app_context, opipe[0], 
				     (XtPointer) XtInputReadMask,
			             command_callback, (caddr_t) entry);
	}
	else
	   entry->output = -1;

#ifdef VFORK
	entry->pid = vfork();      /* create child to process in background */
#else
	entry->pid = fork();      /* create child to process in background */
#endif
	switch (entry->pid)
	{
	   case -1:
		if (errno == EAGAIN)
		{
		   (void) fprintf(stderr, "Could not fork process,  no more \
processes available.  Unix errno = EAGAIN. See UNIX man page describing fork.");
		}
		else if (errno == ENOMEM)
		{
		   (void) fprintf(stderr,"Could not fork process, not enough \
core.  Unix errno = ENOMEM. See UNIX man page describing fork.");
		}
		else
		{
		   (void) fprintf(stderr,"Could not fork process.  Unix errno =\
  %d. See UNIX man page describing fork and errno.", errno);
		}
		entry->pid = 0;
		break;

	   case 0:

		/*
		 *  this is the child.  We should never return after
		 *  calling 
		 */
		if (create_input)
		{
		   close(0); dup(ipipe[0]);
		}

		if (create_output)
		{
		   close(1); dup(opipe[1]);
		}
		execute_routine(temp);
		_exit(1);
		break; 
	}

	if (entry->pid == 0)
	   delete_display(entry);
}



/************************************************************
*
*  Module Name: add_display
*
*      Purpose: This routine is used to add the display to the
*		display_list.
*
*        Input: display - the display to start the command for
*
*       Output: none
*
*   Written By: Mark Young
*
*************************************************************/


DisplayStructure *add_display(display)

char *display;
{
	DisplayStructure *current;


	/*
	 *  create the current image to be added to the xvanimate list.
	 */
	current = (DisplayStructure *) XtCalloc(1, sizeof(DisplayStructure));

	current->next = display_list;
	current->prev = NULL;
	if (display != NULL)
	   current->display = xvf_strcpy(display);

	if (display_list != NULL)
	   display_list->prev = current;

	display_list = current;
	return(current);
}



/************************************************************
*
*  Module Name: delete_display
*
*      Purpose: This routine is used to delete the display to the
*		display_list.  It also kills the process as well
*		as closes the input/output fids.
*
*        Input: current - the display to delete from display_list
*
*       Output: none
*
*   Written By: Mark Young
*
*************************************************************/


delete_display(current)

DisplayStructure *current;
{
	if (current == NULL)
	   return;

	if (current->next == NULL && current->prev == NULL)
	{
	   display_list = NULL;
	}
	else if (current->prev == NULL)
	{
	   current->next->prev = NULL;
	   display_list = current->next;
	}
	else if (current->next == NULL)
	{
	   current->prev->next = NULL;
	}
	else
	{
	   current->next->prev = current->prev;
	   current->prev->next = current->next;
	}

	/*
	 *  Kill the process
	 */
	kill(current->pid, SIGKILL);

	/*
	 *  Remove the input callback and close the input/output file
	 *  descriptors.
	 */
	XtRemoveInput(current->id);
	if (current->input != -1)  close(current->input);
	if (current->output != -1) close(current->output);

	/*
	 *  Free associted memory (cleanup)
	 */
	free(current->display);
	free(current);
}

void command_callback(data, fid, id)

caddr_t   data;
int       *fid;
XtInputId *id;
{
	DisplayStructure *entry = (DisplayStructure *) data;

	int	nbytes, num;
	char	buffer[1024];
	DisplayStructure *current;



	if ((nbytes = read(*fid, buffer, 1024)) <= 0)
	{
	   delete_display(entry);
	   if (display_list == NULL) 
	   {
		FORM_DONE = true;
		exit(0);
	   }
	}
	else if (nbytes > 0)
	{
	   current = display_list;
	   while (current != NULL)
	   {
	      if (current != entry)
	      {
		 if ((num = write(current->input, buffer, nbytes)) != nbytes)
		 {
		    fprintf(stderr,"concert: error writing nbytes = %d errno %d\n", num, errno);
		 }
	      }
	      current = current->next;
	   }
	}
}
