 /*
  * Khoros: $Id: control_util.c,v 1.4 1992/03/20 22:42:03 dkhoros Exp $
  */

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

 /*
  * $Log: control_util.c,v $
 * Revision 1.4  1992/03/20  22:42:03  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 "cantata.h"

static GlyphList *control_runnable();
static GlyphList *control_modified();
static GlyphList *control_disable();

/* >>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
   >>>>                                                       <<<<
   >>>>	    file name:  control_util.c                        <<<<
   >>>>                                                       <<<<
   >>>>   description:                                        <<<<
   >>>>                                                       <<<<
   >>>>      routines:  xvl_control_running()                 <<<<
   >>>>                 xvl_control_modified()                <<<<
   >>>>                 xvl_control_disable()                 <<<<
   >>>>                 xvl_control_break()		      <<<<
   >>>>                                                       <<<<
   >>>>                 xvl_schedule_control()		      <<<<
   >>>>                 xvl_reset_control()                   <<<<
   >>>>                                                       <<<<
   >>>> modifications:					      <<<<
   >>>>                                                       <<<<
   >>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */



/************************************************************
*
* Routine Name:  xvl_control_running
*
*      Purpose:  This routine is used to check if a control glyph
*		 is running.  A control glyph is running if any
*		 of it's loop descendants are currently being
*		 scheduled by autorun.  This is done by checking
*		 all the children of the "output loop" connection
*		 list against the workspace's frontier list.
*
*        Input:  glyph
*
*       Output:  returns true if any descendants or running or
*		 scheduled to be run, otherwise false is returned.
*
*
*   Written By:  Mark Young
*
*************************************************************/

int xvl_control_running(glyph)

Glyph	*glyph;
{
	Glyph	  *child;
	GlyphList *glyphlist = NULL;
	NodeList  *output, *links;

	int	  running, scheduled;


	/*
	 *   Check the children of output loop list to see if any
	 *   descendant is running.
	 */
	running = scheduled = False;
	glyphlist = xvl_add_to_glyphlist(glyph, glyphlist);

	output = glyph->output_list;
	links = output->next->node->links;
	while (links != NULL)
	{
	   child = links->node->glyph;
	   glyphlist = control_runnable(child, glyphlist, &running, &scheduled);

	   /*
	    *  If there is a child already running then return True.
	    */
	   if (running == True)
	   {
	      break;
	   }
	   else if (glyph->input == glyph->input_list->next->node &&
		    scheduled == True)
	   {
	      running = True;
	      break;
	   }
	   links = links->next;
	}
	xvl_destroy_glyphlist(glyphlist);
	return(running);
}

static GlyphList *control_runnable(glyph, glyphlist, running, scheduled)

Glyph	  *glyph;
GlyphList *glyphlist;
int	  *running, *scheduled;
{
	Glyph	  *child;
	Workspace *workspace;
	NodeList  *output, *links;
	int	  child_running, child_scheduled;


	/*
	 *  Check to see if this glyph has already been looked
	 *  at.   If not then put it on the list.
	 */
	if (xvl_check_if_glyphlist(glyph, glyphlist) == True)
	   return(glyphlist);
	else
	   glyphlist = xvl_add_to_glyphlist(glyph, glyphlist);

	/*
	 *  Check to see if this glyph is to be scheduled.  If it
	 *  is then there is glyph that is or will be run within
	 *  this control loop glyph.
	 */
	workspace = glyph->workspace;
	child_running   = xvl_check_if_glyphlist(glyph, workspace->running);
	child_scheduled = xvl_check_if_scheduled(glyph);
	if (child_running == True && !(glyph->run_type == SINK &&
				glyph->exec_type == ONERUN))
	{
	   *running = True;
	   return(glyphlist);
	}
	else if (child_scheduled == True)
	{
	   *scheduled = True;
	   return(glyphlist);
	}
	else
	{
	   /*
	    *  Search the child's output connection to see if any of it's
	    *  descendants will be run.
	    */
	   output = glyph->output_list;
	   while (output != NULL)
	   {
	      links = output->node->links;
	      while (links != NULL)
	      {
	         child = links->node->glyph;
		 glyphlist = control_runnable(child, glyphlist, running,
			scheduled);
		 if (*running == True || *scheduled == True)
		    return(glyphlist);

		 links = links->next;
	      }
	      output = output->next;
	   }
	}
	*running   = False;
	*scheduled = False;
	return(glyphlist);
}



/************************************************************
*
* Routine Name:  xvl_control_modified
*
*      Purpose:  This routine is used to check if a control glyph
*		 needs to be re-run.  A control glyph will need
*		 to be re-run if any of descendants have been 
*		 modified.  This only applies for control glyphs
*		 that are either "count" or "while" loops.  And
*		 only the output loop for modified descendants.
*
*        Input:  glyph
*
*       Output:  returns true if any of the glyph's descendants have
*		 been modified, otherwise false is returned.
*
*
*   Written By:  Mark Young
*
*************************************************************/

int xvl_control_modified(glyph)

Glyph	*glyph;
{
	int	  modified;
	Glyph	  *child;
	GlyphList *glyphlist = NULL;
	NodeList  *output, *links;


	/*
	 *  Make sure the glyph is a control glyph and is either a
	 *  count or while loop.
	 */
	if (((strcmp(glyph->label_str, "count_loop") != 0) &&
	     (strcmp(glyph->label_str, "while_loop") != 0)) ||
	    glyph->type != CONTROL)
	{
	   return(False);
	}

	/*
	 *  Make a quick check to see if this control "loop" glyph has
	 *  been modified, before checking it's descendants.
	 */
	if (glyph->modified)
	   return(True);

	/*
	 *   Check the children of output loop list to see if any
	 *   descendant have been modified
	 */
	modified = False;
	glyphlist = xvl_add_to_glyphlist(glyph, glyphlist);

	output = glyph->output_list;
	links = output->next->node->links;
	while (links != NULL)
	{
	   child = links->node->glyph;
	   glyphlist = control_modified(child, glyphlist, &modified);

	   /*
	    *  If the child has been modified then return True.
	    */
	   if (modified == True)
	   {
	      xvl_reset_control(glyph);
	      break;
	   }

	   links = links->next;
	}
	xvl_destroy_glyphlist(glyphlist);
	return(modified);
}

static GlyphList *control_modified(glyph, glyphlist, modified)

Glyph	  *glyph;
GlyphList *glyphlist;
int	  *modified;
{
	Glyph	  *child;
	NodeList  *output, *links;


	/*
	 *  Check to see if this glyph has already been looked
	 *  at.   If not then put it on the list.
	 */
	if (xvl_check_if_glyphlist(glyph, glyphlist) == True)
	   return(glyphlist);
	else
	   glyphlist = xvl_add_to_glyphlist(glyph, glyphlist);

	/*
	 *  Check to see if this glyph has been modified.  If it
	 *  is then return True meaning we found a modified descendant.
	 */
	if (glyph->modified == True)
	{
	   *modified = True;
	   return(glyphlist);
	}
	else
	{
	   /*
	    *  Search the child's output connection to see if any of it's
	    *  descendants have been modified.
	    */
	   output = glyph->output_list;
	   while (output != NULL)
	   {
	      links = output->node->links;
	      while (links != NULL)
	      {
	         child = links->node->glyph;
		 glyphlist = control_modified(child, glyphlist, modified);
		 if (*modified == True)
		    return(glyphlist);

		 links = links->next;
	      }
	      output = output->next;
	   }
	}
	*modified = False;
	return(glyphlist);
}



/************************************************************
*
* Routine Name:  xvl_control_disable
*
*      Purpose:  This routine is used to disable all loop descendants.
*		 A control glyph must set all descendant's data avail-
*		 ables to false to ensure the proper scheduling and
*		 dispatching.
*
*        Input:  glyph - the control loop whose loop descendants will
*			 be disabled
*
*       Output:  none
*
*
*   Written By:  Mark Young
*
*************************************************************/

xvl_control_disable(glyph)

Glyph	   *glyph;
{
	GlyphList *glyphlist = NULL;


	/*
	 *  Make sure the glyph is a control glyph and is either a
	 *  count or while loop.
	 */
	if (((strcmp(glyph->label_str, "count_loop") != 0) &&
	     (strcmp(glyph->label_str, "while_loop") != 0)) ||
	    glyph->type != CONTROL)
	{
	   return;
	}

	/*
	 *   Check the children of output loop list to see if any
	 *   descendant is running.
	 */
	glyphlist = xvl_add_to_glyphlist(glyph, glyphlist);
	glyphlist = control_disable(glyph, glyphlist);
	xvl_destroy_glyphlist(glyphlist);
}

static GlyphList *control_disable(glyph, disabled)

Glyph	   *glyph;
GlyphList  *disabled;
{
	Node	  *node;
	NodeList  *output, *links;
	Workspace *workspace;


	output = glyph->output_list;
	while (output != NULL)
	{
	   links = output->node->links;
	   while (links != NULL)
	   {
	      node = links->node;
	      if (!xvl_check_if_glyphlist(node->glyph, disabled))
	      {
	         disabled = xvl_add_to_glyphlist(node->glyph, disabled);
	         disabled = control_disable(node->glyph, disabled);
		 node->dav = False;
		 xvl_update_dav(node);
	      }
	      links = links->next;
	   }
	   output = output->next;
	}

	/*
	 *  Delete the glyph from the dispatched list so that we can
	 *  schedule and dispatch all the loop's childeren.
	 */
	workspace = glyph->workspace;
	workspace->dispatched = xvl_delete_from_glyphlist(glyph,
			workspace->dispatched);
	return(disabled);
}




/************************************************************
*
* Routine Name:  xvl_schedule_control
*
*      Purpose:  This routine is used to schedule off the
*		 dispatching of control glyphs.
*
*        Input:  workspace
*
*
*   Written By:  Mark Young
*
*************************************************************/


int xvl_schedule_control(glyph, from, to, node)

Glyph *glyph;
char  *from, *to;
Node  *node;
{
	long	  id;
	int	  pid;
	Boolean	  copy_data;
	char	  *hostname, machname[MaxLength], command[MaxLength],
		  tmp_from[MaxLength], tmp_to[MaxLength];


	if (VStrlen(from) == 0 || VStrlen(to) == 0)
	   copy_data = False;
	else if (strcmp(from, to) == 0)
	   copy_data = False;
	else
	   copy_data = True;

	if (copy_data == True)
	{
	   /*
	    *  Pass it thru the parser to see if need to expand the variable
	    *  first.
	    */
	   id = (long) glyph->workspace->glyphform;
	   if (xve_eval_string(id, from, tmp_from, NULL) == False)
	      strcpy(tmp_from, from);
	   if (xve_eval_string(id, to, tmp_to, NULL) == False)
	      strcpy(tmp_to, to);

	   /*
	    *  Execute the copy on the remote machine or local if no
	    *  machine is specified.
	    */
	   if ((hostname = xvl_get_machname(glyph, machname)) != NULL)
	      (void) sprintf(command, "kcp@%s -i %s -o %s", hostname,
				tmp_from, tmp_to);
	   else
	      (void) sprintf(command, "kcp -i %s -o %s", tmp_from, tmp_to);


	   /*
	    *  run the sucker...
	    */
	   if ((pid = xvf_fork(command, xvl_control_cb, (char *) node)) == 0)
	   {
	      sprintf(command,"cp %s %s", tmp_from, tmp_to);
	      xvf_system(command);
	      xvl_restore_glyph(glyph);
	      xvl_schedule_links(glyph, node);
	      return(DISPATCH_GLYPH_FINISHED);
	   }
	   else
	   {
	      glyph->workspace->running = xvl_add_to_glyphlist(glyph,
			glyph->workspace->running);
	   }
	   glyph->pid = pid;
	}
	else
	{
	   xvl_restore_glyph(glyph);
	   xvl_schedule_links(glyph, node);
	   return(DISPATCH_GLYPH_FINISHED);
	}
	return(DISPATCH_GLYPH_RUNNING);
}



/************************************************************
*
* Routine Name:  xvl_reset_control
*
*      Purpose:  This routine is used to reset control glyphs.
*		 It initializes the "count" and "while" control
*		 loops to their initial values and sets the glyph
*		 to modified so that it will be run again.
*
*        Input:  workspace
*
*
*   Written By:  Mark Young and Carla Williams
*
*************************************************************/


xvl_reset_control(glyph)

Glyph *glyph;
{
	Line_Info        lineinfo;
	xvf_selection    *selection;
	xvf_guide_button *guide;
	xvf_sub_form	 *subform;
	Workspace	 *workspace;

	long	id;
	float   value;
	char	temp[512], error[1024],
		*variable  = NULL,
		*initial   = NULL;


	/*
	 * Make sure the glyph is a control glyph.
	 */
	if (glyph->type != CONTROL)
	   return;

	/*
	 *  Find out what type of control glyph it is.
	 */
	subform = glyph->val.subform;
	guide = xvf_search_sel_guide(subform);

	/*
	 *  Make sure we don't try an access an invalid guide or guide pane.
	 */
	if (guide == NULL)
	   return;
	if (guide->pane == NULL)
	   return;

	/*  Determine which Pane Button was selected  */
	if ((strcmp(guide->button_name,"count_loop_guide_button") == 0))
	{
	   xvf_clear_line_info(&lineinfo);
	   selection = guide->pane->sel_list;

	   while (selection != NULL)
	   {
	      xvl_parse_line(subform->db, selection->index, &lineinfo);
	      if (lineinfo.variable != NULL && lineinfo.literal != NULL)
	      {
	         if (strcmp(lineinfo.variable,"lcv") == 0)
		    variable = xvf_strcpy(lineinfo.literal);
	         else if (strcmp(lineinfo.variable,"init") == 0)
		    initial = xvf_strcpy(lineinfo.literal);
	      }
	      selection = selection->next;
	   }

	   if (variable == NULL || initial == NULL)
	   {
	      (void) sprintf(error,"Error! Incomplete information found for \
control loop '%s'.", glyph->toplevel_name);
	      xvf_error_wait(error, "xvl_reset_control", NULL);
	      return;
	   }
	   else
	   {
	      (void) sprintf(temp,"%s = %s", variable, initial);
	      workspace = glyph->workspace;
	      id = (long) workspace->glyphform;
	      if (!xve_eval_float(id, temp, &value, error))
	      {
		 xvf_error_wait(error, "xvl_reset_control", NULL);
		 return;
	      }
	      glyph->input = NULL;
	      glyph->modified = True;
	      xvl_update_modified(glyph);
	   }
	}
	else if (strcmp(guide->button_name,"while_loop_guide_button") == 0)
	{
	   xvf_clear_line_info(&lineinfo);
	   selection = guide->pane->sel_list;

	   while (selection != NULL)
	   {
	      xvl_parse_line(subform->db, selection->index, &lineinfo);
	      if (lineinfo.variable != NULL && lineinfo.literal != NULL)
	      {
	         if (strcmp(lineinfo.variable,"init") == 0)
		    initial = xvf_strcpy(lineinfo.literal);
	      }
	      selection = selection->next;
	   }

	   if (initial != NULL)
	   {
	      workspace = glyph->workspace;
	      id = (long) workspace->glyphform;
	      if (!xve_eval_float(id, initial, &value, error))
	      {
		 xvf_error_wait(error, "xvl_reset_control", NULL);
		 return;
	      }
	      glyph->input = NULL;
	      glyph->modified = True;
	      xvl_update_modified(glyph);
	   }
	}
	else if ((strcmp(guide->button_name,"if_else_guide_button") == 0) ||
		 (strcmp(guide->button_name,"merge_guide_button") == 0))
	{
	   glyph->modified = True;
	   xvl_update_modified(glyph);
	}
}
