 /*
  * Khoros: $Id: detect.c,v 1.3 1992/03/20 22:49:42 dkhoros Exp $
  */

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

 /*
  * $Log: detect.c,v $
 * Revision 1.3  1992/03/20  22:49:42  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 "xvutils.h"	
#include "vsignal.h"	

static void   check_input_file();
static void   check_input_fid();
static vsignal process_update();

/* >>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
   >>>>                                                       <<<<
   >>>>	    	    file name: detect.c                       <<<<
   >>>>                                                       <<<<
   >>>>   These routines are used to detect whether a file    <<<<
   >>>>   has been changed or input on a fid.  In the case of <<<<
   >>>>   standard input, when new input is detected then we  <<<<
   >>>>   call back the programmer's callback routine.  The   <<<<
   >>>>   programmer also specifies how often we are supposed <<<<
   >>>>   to check the file for modify time, but for a file   <<<<
   >>>>   descriptor we use the X toolkit to listen on the    <<<<
   >>>>   input port.					      <<<<
   >>>>                                                       <<<<
   >>>>                xvf_detect_file()		      <<<<              
   >>>>                xvf_detect_fid()                       <<<<
   >>>>                xvf_delete_detect()                    <<<<
   >>>>                check_input_file()                     <<<<
   >>>>                check_input_fid()                      <<<<
   >>>>                                                       <<<<
   >>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */

typedef struct _xvf_detect
{
	int	id;
	time_t  time;
	char	*filename;
	int	fid, interval;
	int	(*routine)();
	caddr_t	clientData;

	int    force_callback;

	struct _xvf_detect *next, *prev;
} xvf_detect;

static xvf_detect *detect_list = NULL;




/************************************************************
*
*  MODULE NAME: xvf_detect_file
*
*      PURPOSE: Adds a handler to detect if a file has been changed.
*
*       OUTPUT: None
*
*    CALLED BY: Application programmer
*
*   WRITTEN BY: Mark Young
*
*
*************************************************************/


int xvf_detect_file(file, argtime, routine, clientData)

char	*file;
float	argtime;
int	(*routine)();
caddr_t clientData;
{
	struct stat   buf;
	xvf_detect    *entry;
	char	      *filename, error[512];


	/*
	 *  Get the last modified time of the file in question.
	 */
	if (!(filename = vfullpath(file, NULL, NULL)))
	{
	   sprintf(error,"Error!  Invalid filename found.");
	   xvf_error_wait(error, "xvf_detect_file", NULL);
	   return(FALSE);
	}

	if (!(entry = (xvf_detect *) calloc(1, sizeof(xvf_detect))))
	{
	   xvf_error_wait("Out of memory!", "xvf_detect_file", NULL);
	   return(FALSE);
	}

	if (stat(filename, &buf) != -1)
	   entry->time = buf.st_mtime;
	else
	   entry->time = time(NULL);

	entry->next = detect_list;
	entry->prev = NULL;
	if (detect_list != NULL) 
	   detect_list->prev = entry;

	detect_list = entry;

	/*
	 *  update the rest of detect structure info.  We also need
	 *  compute the update interval in terms of millaseconds instead
	 *  of seconds.
	 */
	entry->interval = argtime * 1000;
	entry->filename = filename;
	entry->fid	= -1;
	entry->routine  = routine;
	entry->clientData  = clientData;
	entry->id = (int) XtAppAddTimeOut(xvf_app_context, entry->interval,
				   check_input_file, (caddr_t) entry);

	signal(SIGCONT, process_update);
	kill(getppid(), SIGCONT);
	return(entry->id);
}

static vsignal process_update(signal, flags, context)

int     signal, flags;
struct  sigcontext *context;
{
	xvf_detect *entry = detect_list;


#ifdef DEBUG
	printf("SIGCONT called for process_update\n");
#endif
	while (entry != NULL)
	{
	   if (entry->filename != NULL && entry->force_callback == False)
	   {
	      /*
	       *  Set the forced callback so that all files are designated
	       *  touched.
	       */
	      entry->force_callback = True;

	      /*
	       *  Re-add check_input_file to the time out handler to call us
	       *  back immediately.
	       */
	      XtRemoveTimeOut((XtIntervalId) entry->id);
	      entry->id = (int) XtAppAddTimeOut(xvf_app_context, 0,
				check_input_file, (caddr_t) entry);
	   }
	   entry = entry->next;
	}
}



/************************************************************
*
*  MODULE NAME: xvf_detect_fid
*
*      PURPOSE: Adds a handler to detect if input has been detected
*		on the given file descriptor.
*
*       OUTPUT: None
*
*    CALLED BY: Application programmer
*
*   WRITTEN BY: Mark Young
*
*
*************************************************************/



int xvf_detect_fid(fid, routine, clientData)

int	fid;
int	(*routine)();
caddr_t clientData;
{
	xvf_detect     *entry;


	if (!(entry = (xvf_detect *) calloc(1, sizeof(xvf_detect))))
	{
	   xvf_error_wait("Out of memory!", "xvf_detect_file", NULL);
	   return(FALSE);
	}

	entry->time	  = time(NULL);
	entry->fid	  = fid;
	entry->filename   = NULL;
	entry->routine    = routine;
	entry->clientData = clientData;

	entry->next = detect_list;
	entry->prev = NULL;
	if (detect_list != NULL) 
	   detect_list->prev = entry;

	detect_list = entry;

	/*
	 *  if the file is being readin from a fid then we want to "listen"
	 *  on the given fid.
	 */
	entry->id = (int) XtAppAddInput(xvf_app_context, fid, 
					(XtPointer) XtInputReadMask,
				   check_input_fid, (caddr_t) entry);
	return(entry->id);
}



/************************************************************
*
*  MODULE NAME: xvf_delete_detect
*
*      PURPOSE: deletes a detect for a file or fid specified by the
*		specified id.  We know whether to delete 
*
*       OUTPUT: None
*
*    CALLED BY: Application programmer
*
*   WRITTEN BY: Mark Young
*
*
*************************************************************/


xvf_delete_detect(id)

int	id;
{
	xvf_detect     *entry;


	if (detect_list == NULL)
	   return;

	entry = detect_list;
	while (entry->next != NULL && entry->id != id)
	   entry = entry->next;


	if (entry->id == id)
	{
	   if (entry->next == NULL && entry->prev == NULL)
	      detect_list = NULL;
	   else if (entry->prev == NULL)
	   {
	      entry->next->prev = NULL;
	      detect_list = entry->next;
	   }
	   else if (entry->next == NULL)
	   {
	      entry->prev->next = NULL;
	   }
	   else
	   {
	      entry->next->prev = entry->prev;
	      entry->prev->next = entry->next;
	   }

	   if (entry->filename != NULL)
	   {
	      free(entry->filename);
	      XtRemoveTimeOut((XtIntervalId) entry->id);
	   }
	   else
	   {
	      XtRemoveInput((XtInputId) entry->id);
	   }
	   free(entry);
	}
}



/************************************************************
*
*  MODULE NAME: check_input_file
*
*      PURPOSE: Checks to see if a file has changed.  If so 
*		we call the user's callback routine with the
*		id and client data.  If the user returns true
*		then we update the time otherwise we will check
*		the file again with the same time.
*
*       OUTPUT: None.  Calls the user's callback routine to perform
*		all necessary output.
*
*    CALLED BY: X toolkit thru the XtAppAddTimeOut mechanism
*
*   WRITTEN BY: Mark Young
*
*
*************************************************************/


static void  check_input_file(data, id)

caddr_t		data;
XtIntervalId	*id;
{
	struct stat buf, tmp_buf;
	int	    status, (*routine)();
	xvf_detect  *entry = (xvf_detect *) data;


	/*
	 *  Get the stat's for the entry filename, unless we have a force
	 *  callback.  If a force callback that simply do the callback no
	 *  matter what state we are in.
	 */
	if (stat(entry->filename, &buf) != -1 || entry->force_callback)
	{
	   /*
	    *  check to see if the file has been modified since the last
	    *  time we read it.
	    */
	   if (entry->time != buf.st_mtime || entry->force_callback)
	   {
	      /*
	       *  since the file has been modified call the user's callback
	       *  routine and update the read time.
	       */
	      routine    = entry->routine;

	      tmp_buf.st_mtime = entry->time;
	      entry->time = buf.st_mtime;
	      status = routine(entry->id, entry->filename, entry->clientData);
	      if (status == FALSE)
	         entry->time = tmp_buf.st_mtime;
	      else
	         entry->force_callback = False;
	   }
	}

	/*
	 *  Re-add check_input_file to the time out handler.
	 */
	entry->id = (int) XtAppAddTimeOut(xvf_app_context, entry->interval,
				     check_input_file, data);
}



/************************************************************
*
*  MODULE NAME: check_input_fid
*
*      PURPOSE: Action routine used to detect when new input has
*		been detected on a file id.  When data is detected 
*		we call the user's callback routine with the
*		id and client data.
*
*       OUTPUT: None.  Calls the user's callback routine to perform
*		all necessary output.
*
*    CALLED BY: X toolkit thru the XtAppAddInput mechanism
*
*   WRITTEN BY: Mark Young
*
*
*************************************************************/


static void check_input_fid(data, fid, id)

caddr_t		data;
int		*fid;
XtInputId	*id;
{
	int	    status, (*routine)();
	xvf_detect  *entry = (xvf_detect *) data;


	/*
	 *  since we have detected input on the fid, call back the
	 *  user's callback routine and if they return true we then
	 *  update the read time to the current time.
	 */
	routine    = entry->routine;

	status = routine(entry->id, entry->fid, entry->clientData);
	if (status == TRUE)
	   entry->time = time(NULL);
}
