 /*
  * Khoros: $Id: util.c,v 1.4 1992/03/20 22:43:45 dkhoros Exp $
  */

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

 /*
  * $Log: util.c,v $
 * Revision 1.4  1992/03/20  22:43:45  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"


/* >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
   >>>>								<<<<
   >>>>	    file name:  util.c					<<<<
   >>>>								<<<<
   >>>>   description:						<<<<
   >>>>								<<<<
   >>>>      routines:  xvl_parse_line()			<<<<
   >>>>                 xvl_change_workspace()			<<<<
   >>>>                 xvl_unlink_tempfile()			<<<<
   >>>>                 xvl_query_position()			<<<<
   >>>>                 xvl_tempnam()				<<<<
   >>>>                 xvl_help()				<<<<
   >>>>                 xvl_copy()				<<<<
   >>>>                 xvl_move()				<<<<
   >>>>                 xvl_query_widget()			<<<<
   >>>>                 xvl_clear_dav()				<<<<
   >>>>                 xvl_print_signal()			<<<<
   >>>>                 xvl_draw_label()			<<<<
   >>>>                 xvl_clear_label()			<<<<
   >>>>                 xvl_find_onode()			<<<<
   >>>>								<<<<
   >>>> modifications:						<<<<
   >>>>								<<<<
   >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */



/************************************************************
*
* Routine Name:  xvl_parse_line
*
*      Purpose:  This routine is used to set a desired guide
*		 button for a given subform.  The user supplies
*		 the subform and the guide button they wish to
*		 make active.  xvf_set_guide then races thru
*		 the old guide list clearing the current selected
*		 guide pane and selecting the desired one.
*
*        Input:  database - database to be parsed
*		 index    - index into the database to be parsed.
*
*	Output:  lineinfo - lineinfo structure to be filled.
*
*
*   Written By: Mark Young
*
*************************************************************/

xvl_parse_line(database, index, lineinfo)

char	  **database;
int	  index;
Line_Info *lineinfo;
{
	xvf_gen_parse(database[index], lineinfo);

	if (lineinfo->selected == True)
	{
	   lineinfo->selected = False;
	   xvf_gen_deparse(lineinfo, database, index);
	   lineinfo->selected = True;
	}
}



/************************************************************
*
* Routine Name:  xvl_change_workspace
*
*      Purpose:  This routine is used to change the workspace associated
*		 with for a given menu form.  If no workspace is found we
*		 return NULL otherwise we return the widget as well as
*		 change the name given with the workspace, it's width and
*		 height.
*
*        Input:  
*
*
*   Written By: Mark Young
*
*************************************************************/


Widget xvl_change_workspace(form, name, width, height)
xvf_form *form;
char	 *name;
{
	Line_Info    lineinfo;
	xvf_sub_form *subform;
	char	     temp[50];


	xvf_clear_line_info(&lineinfo);
	subform = form->subform;

	while (subform != NULL)
	{
	   if (subform->type == WorkWidget)
	   {
	      if (name != NULL)
	      {
		 if (xvf_strlen(name) != 0)
	         {
 	            xvf_change_input(form, subform->button_index,
				xvf_title_chng, name, 0);
	         }
		 else
	         {
 	            xvf_change_input(form, subform->button_index,
				xvf_title_chng, " ", 0);
	         }
	      }

	      if (width > 0)
	      {
		 (void) sprintf(temp,"%d", width);
 	         xvf_change_input(form, subform->button_index, 
				  xvf_width_chng, temp, 0);
	      }

	      if (height > 0)
	      {
		 (void) sprintf(temp,"%d", height);
 	         xvf_change_input(form, subform->button_index, 
				  xvf_height_chng, temp, 0);
	      }
	      return(subform->button);
	   }
 
	   subform = subform->next_subform;
	}
	return(NULL);
}



/************************************************************
*
* Routine Name:  xvl_unlink_tempfile
*
*      Purpose:  This routine is used to unlink a temporary output
*		 connection filename.
*
*        Input:  node - the output connection node
*
*
*   Written By: Mark Young
*
*************************************************************/


xvl_unlink_tempfile(node)

Node *node;
{
	char	  *machine, *filename, temp[MaxLength], machname[MaxLength];


	/*
	 *  Perform some error checking to make sure that we do not
	 *  remove file that wasn't created by cantata.
	 */
	if (node == NULL)
	   return;
	else if (node->filename == NULL || node->temp_file == False)
	   return;

	if ((machine = xvl_get_machname(node->glyph, machname)) != NULL)
	{
	   (void) sprintf(temp, "%s@%s", node->filename, machine);
	   filename = temp;
	}
	else
	   filename = node->filename;

	kunlink(filename);
}



/************************************************************
*
* Routine Name: xvl_query_position
*
*      Purpose: This routine is used to tell query the position
*		of the window cursor to the desired widget.
*
*       Output: returns the x and y position of the cursor. If
*		the cursor is outside of the window then the
*		cursor is clipped to that window.
*
*    CALLED BY:
*
*   WRITTEN BY: Mark Young
*
*
*************************************************************/


xvl_query_position(parent, widget, x, y, clip)

Widget	 parent, widget;
Position *x, *y;
int	 clip;
{
	Display	 *display = XtDisplay(parent);
	Window	 window   = XtWindow(parent);

	Window   root, child;
	int      rx, ry, wx, wy;
	unsigned int width, height, widget_width, widget_height,
		     border, depth, buttons;


	/*
	 * Query the pointer to see where we are relative to the window.
	 */
	XQueryPointer(display, window, &root, &child, &rx, &ry, &wx, &wy,
			&buttons);
	*x = wx;
	*y = wy;

	/*
	 *  Get the geometry of that window so that we might clip the mouse to
	 *  that window.  We do this so that objects cannot be dragged outside
	 *  the window and thus be lost forever.
	 */
        if ( !XGetGeometry(display, window, &root, &wx, &wy, &width,
                           &height, &border, &depth))
        {
           xvf_error_wait("Warning! Glyph does not exist.  This should not be \
possible.", "xvl_query_position", NULL);
	   return;
        }

	widget_width = widget_height = 0;
	if (widget != NULL && clip)
	{
	   display = XtDisplay(widget);
	   window  = XtWindow(widget);
           if ( !XGetGeometry(display, window, &root, &wx, &wy, &widget_width,
                              &widget_height, &border, &depth))
           {
              xvf_error_wait("Warning! Glyph does not exist.  This should not \
be possible.", "xvl_query_position", NULL);
	      return;
           }
	}

	/*
	 *  Clip the position to the window.
	 */
	if (clip)
	{
	   if (*x < 0)
	      *x = 0;
	   else if (*x > (width - widget_width))
	      *x = width - widget_width;

	   if (*y < 0)
	      *y = 0;
	   else if (*y > (height - widget_height))
	      *y = height - widget_height;
	}
}



/************************************************************
*
* Routine Name:  xvl_tempnam
*
*      Purpose:  This routine is used to create a temporary
*		 file from the supplied template "filename".
*		 xvl_tempnam calls ktempnam() which returns
*		 a path and filename to a directory which
*		 the current user has write permission.
*
*
*        Input:  filename - the template filename.
*
*
*   Written By: Mark Young
*
*************************************************************/


char *xvl_tempnam(template, transport, machine)

char	*template;
char	*transport;
char	*machine;
{
	char	*filename, temp[MaxLength];


	/*
	 *  Add the template and machine in which to create the template
	 */
	if (transport != NULL)
	   (void) sprintf(temp, "%s=%s", transport, template);
	else
	   (void) strcpy(temp, template);

	/*
	 *  Need to create an empty file (touch), so that the
	 *  next call to ktempnam() will  create a different
	 *  file.  We call ktempnam to give us a unique temporary
	 *  filename and then we open/close the file to create it.
	 */
	if (!(filename = ktempnam(machine, temp)))
	   return(NULL);

	return(filename);
}



/************************************************************
*
* Routine Name:  xvl_help
*
*      Purpose:  This routine is used to display a file using
*		 the xvform help widget.  We use the xvhelp
*		 process 
*
*        Input:  filename - the 
*
*
*   Written By: Mark Young
*
*************************************************************/


xvl_help(filename, label, temp_file)

char	*filename, *label;
Boolean temp_file;
{
	if (xvf_create_online_help(filename, label) == True)
	{
	   if (temp_file == True)
	      unlink(filename);
	   return;
	}
}



/************************************************************
*
* Routine Name:  xvl_copy
*
*      Purpose:  This routine is used to copy the contents
*		 from one file to another.
*
*        Input:  from - the file to be copied from
*		 to   - the file to be copied to
*
*
*   Written By: Mark Young
*
*************************************************************/


xvl_copy(from, to)

char	*from, *to;
{
	char command[MaxLength];


	sprintf(command,"kcp -i %s -o %s", from, to);
	if (xvf_fork(command, NULL, NULL) == False)
	{
	   sprintf(command,"cp %s %s", from, to);
	   xvf_system(command);
	}
}



/************************************************************
*
* Routine Name:  xvl_move
*
*      Purpose:  This routine is used to copy the contents
*		 from one file to another.
*
*        Input:  from - the file to be copied from
*		 to   - the file to be copied to
*
*
*   Written By: Mark Young
*
*************************************************************/


xvl_move(from, to)

char	*from, *to;
{
	char command[MaxLength];
	caddr_t calldata;


	sprintf(command,"kcp -i %s -o %s", from, to);
	calldata = (caddr_t) xvf_strcpy(from);
	if (xvf_fork(command, xvl_move_cb, calldata) == False)
	{
	   sprintf(command,"cp %s %s", from, to);
	   xvf_system(command);
	   free(calldata);
	   kunlink(from);
	}
}
 


/************************************************************
*
* Routine Name: xvl_query_widget
*
*      Purpose: This routine is used to query the position
*		of a widget to it's parent's position.
*
*       Output: None
*
*    CALLED BY:
*
*   WRITTEN BY: Mark Young
*
*
*************************************************************/


xvl_query_widget(widget, xposition, yposition, width, height)

Widget	  widget;
Position  *xposition, *yposition;
Dimension *width, *height;
{
	int	  xi, yi;
	Window    root;
	Position  x, y, xpos, ypos;
	unsigned  int w, h, depth, border;



	if (xposition != NULL || yposition != NULL)
	{
	   XtTranslateCoords(XtParent(widget), 0, 0, &x, &y);
	   XtTranslateCoords(widget, 0, 0, &xpos, &ypos);

	   if (xposition != NULL) *xposition = xpos - x;
	   if (yposition != NULL) *yposition = ypos - y;
	}

	if (width != NULL || height != NULL)
	{
           if (!XGetGeometry(XtDisplay(widget), XtWindow(widget), &root, &xi,
			&yi, &w, &h, &border, &depth))
           {
              return;
           }

	   if (width  != NULL) *width  = w;
	   if (height != NULL) *height = h;
	}
}



/************************************************************
*
* Routine Name: xvl_clear_dav
*
*      Purpose: This routine is used to clear a glyph's output
*		data available list.
*
*        Input: glyph - the glyph to be cleared.
*
*       Output: none
*
*
*   Written By: Mark Young
*
*************************************************************/


static GlyphList *clear_dav(glyph, connection, glyphlist)

Glyph *glyph;
Node  *connection;
GlyphList *glyphlist;
{
	Glyph     *temp;
	Node	  *node;
	NodeList  *nodelist, *links;
	int	  runnable;
	Workspace *workspace, *attributes;


	/*
	 *  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);

	/*
	 *  Get the glyph's workspace and the corresponding attributes
	 *  workspace.
	 */
	workspace = glyph->workspace;
	attributes = xvl_get_attributes(workspace);

	/*
	 *  Comment glyphs and glyphs that are of run type SOURCE are immunue
	 *  to being modified and therefore should not reflect a modified
	 *  state.
	 */
	if (glyph->type == COMMENT || (glyph->exec_type == NORUN &&
	     glyph->run_type == SOURCE))
	   runnable = False;
	else
	   runnable = True;

	/*
	 *  If the node is not part of the control loop then clear
	 *  it's data availables.
	 */
	if (xvl_check_if_loop_node(glyph, connection) == False)
	{
	   nodelist = glyph->output_list;
	   while (nodelist != NULL)
	   {
	      node = nodelist->node;
	      if (node->dav == True)
	      {
	         if (runnable == True)
	         {
		    node->dav = False;
	            xvl_update_dav(node);
	         }
	         links = node->links;
	         while (links != NULL)
	         {
		    if (runnable == True)
		    {
		       links->node->dav = False;
	               xvl_update_dav(links->node);
		    }

		    temp = links->node->glyph;
		    xvl_clear_dav(temp, links->node);
		    links = links->next;
	         }
	      }
	      nodelist = nodelist->next;
	   }
	}

	if (xvl_check_if_glyphlist(glyph, workspace->running) == True &&
	    attributes->demand_driven == True)
	{
	   xvl_demand_scheduler(glyph);
	}
	return(glyphlist);
}

xvl_clear_dav(glyph, connection)

Glyph *glyph;
Node  *connection;
{
	GlyphList *glyphlist = NULL;

	glyphlist = clear_dav(glyph, connection, glyphlist);
	xvl_destroy_glyphlist(glyphlist);
}




/************************************************************
*
* Routine Name: xvl_find_glyph_label
*
*      Purpose: This routine is used to find the glyph label
*		for COMMENT, COMMAND, and GLYPH
*
*        Input: None
*
*       Output: Sync's the display
*
*
*   Written By: Mark Young
*
*************************************************************/


char *xvl_find_subform_label(subform)

xvf_sub_form     *subform;
{
	char *label = NULL;

	Line_Info	 lineinfo;
	xvf_guide_button *guide;
	xvf_selection	 *selection;


	/* search for title */
	xvf_clear_line_info(&lineinfo);

	if (!(guide = xvf_search_sel_guide(subform)))
	{
	   xvf_gen_parse(subform->db[subform->index], &lineinfo);
	   label = xvf_strcpy(lineinfo.variable);
	}
	else
	{
	   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, "label") == 0)
		 {
	            label = xvf_strcpy(lineinfo.literal);
		    break;
		 }
	      }
	      selection = selection->next;
	   }
	}
	return(label);
}



/************************************************************
*
* Routine Name: xvl_set_color
*
*      Purpose: This routine is used to set the pixel color for
*		the desired pixel value if the pixel value is
*		set (a pixel value is valid if it is not set to
*		~0L).
*
*        Input: args - the arg list
*		indx - the current index
*		resource - the arg resource
*		value    - the pixel value.
*
*       Output: none
*
*
*   Written By: Mark Young
*
*************************************************************/


xvl_set_color(args, indx, resource, value)

Arg  *args;
int  *indx;
char  *resource;
Pixel value;
{
	if (value != ~0L && resource != NULL)
	{
	   XtSetArg(args[*indx], resource, value); (*indx)++;
	}
}



/************************************************************
*
* Routine Name: xvl_init_color
*
*      Purpose: This routine is used retrieve the foreground,
*		background, and border color from a widget
*		if the the desired resource is not NULL.
*
*        Input: widget - the widget to retrieve the info from
*
*       Output: foreground - the foreground pixel value
*		background - the background pixel value
*		border_color - the border_color pixel value
*
*
*   Written By: Mark Young
*
*************************************************************/


xvl_init_color(widget, foreground, background, border_color)

Widget widget;
Pixel  *foreground, *background, *border_color;
{
	int	i = 0;
	Arg	args[MaxArgs];


	if (widget == NULL)
	   return;

	if (foreground != NULL)
	{
	   XtSetArg(args[i], XtNforeground, foreground);	i++;
	}

	if (background != NULL)
	{
	   XtSetArg(args[i], XtNbackground, background);	i++;
	}

	if (border_color != NULL)
	{
	   XtSetArg(args[i], XtNborderColor, border_color);	i++;
	}

	if (i > 0)
	   XtGetValues(widget, args, i);
}



/************************************************************
*
* Routine Name: xvl_print_signal
*
*      Purpose: This routine is used to print a formatted message
*		about the supplied signal.  When a process terminates
*		a termination signal is returned.  This routines prints
*		out which termination signal has occurred to the desired
*		"file".
*
*        Input: file - the file pointer to format the message.
*		signal - the termination signal.
*
*       Output: none
*
*
*   Written By: Mark Young
*
*************************************************************/


xvl_print_signal(file, signal)

FILE *file;
int  signal;
{
	switch (signal)
	{
	   case SIGILL:
		fprintf(file,"Illegal instruction...\n");
		break;

	   case SIGSEGV:
		fprintf(file,"Segmentation violation...\n");
		break;

	   case SIGINT:
		fprintf(file,"Routine interupted...\n");
		break;

	   case SIGKILL:
		fprintf(file,"Routine killed...\n");
		break;

	   case SIGTERM:
		fprintf(file,"Software terminated from signal...\n");
		break;

	   case SIGFPE:
		fprintf(file,"Floating point exception...\n");
		break;

	   case SIGBUS:
		fprintf(file,"Bus error...\n");
		break;

	   case SIGPIPE:
		fprintf(file,"Write on a pipe with no process to read it...\n");
		break;

	   default:
		fprintf(file,"Unknown signal number (%d)....  Check signal.3\n",
			 signal);
	}
}



/************************************************************
*
* Routine Name: xvl_draw_label
*
*      Purpose: This routine is to draw the label below the
*		glyph.
*
*        Input: glyph - the glyph in which to draw the string
*		label - the label to be drawn
*
*       Output: Sync's the display
*
*
*   Written By: Mark Young
*
*************************************************************/


int xvl_draw_label(glyph, label)

Glyph *glyph;
char  *label;
{
	Window      draw;
	Display     *display;
	int	    x, y, width, height, length;
	Workspace   *workspace = glyph->workspace;

	XCharStruct ov_return;
	int	    ascent, descent, dir_return;


	if (workspace->draw == NULL)
	   return(False);

	draw = XtWindow(workspace->draw);
	display = XtDisplay(workspace->draw);

	if ((length = xvf_strlen(label)) <= 0)
	   return(False);

	/*
	 *  Draw the string
	 */
	XTextExtents(workspace->name_font, label, length, &dir_return,
                     &ascent, &descent, &ov_return);
	height = ascent + descent;
	width = XTextWidth(workspace->name_font, label, length);

	x = glyph->xpos - (width - ((int) glyph->width))/2;
	y = glyph->ypos + height + glyph->height + 4;
	XDrawString(display, draw, workspace->gc_name, x, y, label, length);
}



/************************************************************
*
* Routine Name: xvl_clear_label
*
*      Purpose: This routine is to clear the label below the
*		glyph.
*
*        Input: glyph - the glyph in which to draw the string
*		label - the label to be cleared
*
*       Output: Sync's the display
*
*
*   Written By: Mark Young
*
*************************************************************/


int xvl_clear_label(glyph, label)

Glyph *glyph;
char  *label;
{
	Window      draw;
	Display     *display;
	int	    x, y, width, height, length;
	Workspace   *workspace = glyph->workspace;

	XCharStruct ov_return;
	int	    ascent, descent, dir_return;


	if (workspace->draw == NULL)
	   return(False);

	draw = XtWindow(workspace->draw);
	display = XtDisplay(workspace->draw);

	if ((length = xvf_strlen(label)) <= 0)
	   return(False);

	/*
	 *  Clear the string
	 */
	XTextExtents(workspace->name_font, label, length, &dir_return,
                     &ascent, &descent, &ov_return);
	height = ascent + descent + 10;
	width = XTextWidth(workspace->name_font, label, length) + 10;

	x = glyph->xpos - (width - ((int) glyph->width))/2 - 5;
	y = glyph->ypos + glyph->height - 2;
	XClearArea(display, draw, x, y, width, height, True);
	return(True);
}



/************************************************************
*
* Routine Name: sync
*
*      Purpose: This routine is used with dbx to sync the
*		display.
*
*        Input: None
*
*       Output: Sync's the display
*
*
*   Written By: Mark Young
*
*************************************************************/


int xvl_sync()
{
	XSync(display, False);
	return(True);
}



/************************************************************
*
*  Routine Name:  xvl_find_onode
*
*      Purpose:  finds the output connection that corresponds
*		 to the restore filename.
*
*	 Input:  filename  -  under which temp files were saved
*		 glyphs    -  the glyphlist that contains the new
*			      filenames for the temporary output
*			      connections.
*
*       Output:  returns the output node if we can find the
*		 output connection that contains the restore
*		 name, otherwise it returns false.
*
*
*   Written By:   Mark Young
*
*************************************************************/


Node *xvl_find_onode(filename, glyphs)

char	  *filename;
GlyphList *glyphs;
{
	Glyph	  *glyph;
	GlyphList *macrolist;

	Node	  *onode;
	NodeList  *nodelist;


	while (glyphs != NULL)
	{
	   /*
	    *  If the glyph is a procedure then check it's list to
	    *  see if the file is located in the procedure's workspace.
	    *
	    *  Otherwise check the glyph's output list to see if it's
	    *  in the current glyph's output connections.
	    */
	   glyph = glyphs->glyph;
	   if (glyph->type == PROCEDURE)
	   {
	      macrolist = glyph->val.macro->glyphs;
	      if ((onode = xvl_find_onode(filename, macrolist)) != NULL)
		 return(onode);
	   }
	   else
	   {
	      nodelist = glyph->output_list;
	      while (nodelist != NULL)
	      {
		 onode = nodelist->node;
	         if (onode->restore_name != NULL)
	         {
	            if (strcmp(filename, onode->restore_name) == 0)
	               return(onode);
	         }

		 if (onode->filename != NULL)
		 {
	            if (strcmp(filename, onode->filename) == 0)
	               return(onode);
		 }
	         nodelist = nodelist->next;
	      }
	   }
	   glyphs = glyphs->next;
	}
	return(NULL);
}
