/*---------------------------------------------------------------------
 * $Date: 2009-08-18 15:23:20 $         $Revision: 1.2 $
 *---------------------------------------------------------------------
 *             Copyright (c) 1992, Visual Edge Software Ltd.
 *
 * ALL  RIGHTS  RESERVED.  Permission  to  use,  copy,  modify,  and
 * distribute  this  software  and its documentation for any purpose
 * and  without  fee  is  hereby  granted,  provided  that the above
 * copyright  notice  appear  in  all  copies  and  that  both  that
 * copyright  notice and this permission notice appear in supporting
 * documentation,  and that  the name of Visual Edge Software not be
 * used  in advertising  or publicity  pertaining to distribution of
 * the software without specific, written prior permission. The year
 * included in the notice is the year of the creation of the work.
 *---------------------------------------------------------------------
 *   File_Description_Section
 *--------------------------------------------------------------------*/
/*
.VERSION
 090813         last modif KB
*/


/*--- include files ---*/
#include <unistd.h>

#include "uxproto.h"
#include "version.h"
#include "subproc.h"
#include "sp_spmd.h"
#include "sp_pty.h"

/*--- macro symbolic constants ---*/

/*--- macro functions ---*/

/*--- types ---*/

/*--- external functions ---*/

extern void UxTextAppend();


/*--- external variables ---*/

/*--- global variables ---*/
SubprocInfo_t *UxSpArray[MAX_SUBPROC];
SubprocMgr_t UxSubprocMgr;

/*--- file global variables ---*/
M_FILE_VERSION("$Header")

/*--- forward declaration of static functions ---*/


/*----------------------------------------------------------------------
 * NAME:	<int UxSetSubprocExitCallback(index)>
 * DESCRIPTION:	<Set the exit callback function for the process>
 * PARAMETERS: 	handle     h               - handle to subproc
 *        	void       (*fnt)()        - exit callback function
 * RETURN:	ERROR or NO_ERROR
 * EXT REFS:	UxSpArray	
 * EXT EFFECTS:	UxSpArray	
 * ASSUMPTIONS:
 * REVISIONS:	05/01/93        fix3810 clean-up	
		dd/mm/yy	fix#	fix_description
 *--------------------------------------------------------------------*/
int	UxSetSubprocExitCallback(h, fnt)
handle	h;
void	(*fnt)();
{
	SubprocInfo_t *sp;

	if(UxCheckIndex(h) == ERROR)
		return(ERROR);

	sp = UxSpArray[h];
	sp->exit_cb = fnt;
	return(NO_ERROR);
}

/*----------------------------------------------------------------------
 * NAME:	<int             UxDeleteSubproc(index)>
 * DESCRIPTION:	<Delete the structure and set the entry to 0>
 * PARAMETERS:  handle          index       - index  into array of subprocs
 * RETURN:	ERROR or NO_ERROR	
 * EXT REFS:	UxSpArray	
 * EXT EFFECTS:	UxSpArray	
 * ASSUMPTIONS:
 * REVISIONS:	05/01/93        fix3810 clean-up	
		02/04/93	fix4074	don't execute if subproc exiting 
		dd/mm/yy	fix#	fix_description
 *--------------------------------------------------------------------*/
int UxDeleteSubproc(index)
handle index;
{

	if (UxSpArray[index] == (SubprocInfo_t *) 0) {
		return ERROR;
	}

	if (UxSpArray[index]->subprocStatus != EXITING) {

		if (UxCheckIndex(index) == ERROR) {
			return ERROR;
		}

		/* Exit the process if running and close tty/pty 
         	 * remove the XInput fd */
		UxExitSubproc(index);

		if(UxSpArray[index]->process)
			UxFree(UxSpArray[index]->process);

		if(UxSpArray[index]->defarg)
			UxFree(UxSpArray[index]->defarg);

		UxFree(UxSpArray[index]);
		UxSpArray[index] = (SubprocInfo_t *) 0;
	} else {
		error_msg(CGETS(MS_MISC1_PROCESS_EXITING, 
				DS_MS_MISC1_PROCESS_EXITING));
	}
	return NO_ERROR;
}
/*----------------------------------------------------------------------
 * NAME:	<int             UxDelayedDeleteSubproc(index)>
 * DESCRIPTION:	<Delayed delete of the subproc>
 * PARAMETERS:  handle          thisHandle      - index into array of subprocs
 * RETURN:	void
 * EXT REFS:	
 * EXT EFFECTS:	
 * ASSUMPTIONS:
 * REVISIONS:	29/04/93	- created
		dd/mm/yy	fix#	fix_description
 *--------------------------------------------------------------------*/
int UxDelayedDeleteSubproc (thisHandle)
handle thisHandle;
{
	static handle lastHandle = -1;

	/*-----------------------------------------------------
	 * Safety check. Allow a value of -1 for handles. It'll
	 * be used to force the previous delete.
	 *-----------------------------------------------------*/

	if ((thisHandle != -1) && 
	    (UxCheckIndex (thisHandle) == ERROR)) {
		return(ERROR);
	}

	/*-----------------------------------------------------
	 * If we're already in a DELAYED_EXIT, just return
	 * Otherwise, the next time UxDelayedDeleteSubproc is
	 * called it'll try to delete an already deleted handle.
	 *-----------------------------------------------------*/
	if ((thisHandle == -1) ||
	    (UxSpArray[thisHandle]->subprocStatus != DELAYED_EXIT)) {

		/*-----------------------------------------------------
		 * The user might have called UxDeleteSubproc after
		 * calling UxDelayedDeleteSubproc on the same handle.
		 * In that case, UxSpArray[lastHandle] is NULL.
		 *-----------------------------------------------------*/
		if (lastHandle != -1)  {
			UxDeleteSubproc (lastHandle);
		}

		lastHandle = thisHandle;
		if (thisHandle != -1) {
			UxSpArray[thisHandle]->subprocStatus = DELAYED_EXIT;
		}
	}
	return NO_ERROR;
}

/*----------------------------------------------------------------------
 * NAME:	<handle   UxCreateSubproc(process, defarg, fnt)>
 * DESCRIPTION:	<creates a subprocess entry and returns a handle to it>
 * PARAMETERS: char  *process        - subprocess to be created
                     *defarg         - default argument to the
                                       subprocess - overridden by
                                       argument passed to UxRunSubproc
       	       void  (*fnt)()        - output function caused when
                                       output present from subproc
 * RETURN:	subprocess handle = index into subproc arrray	
 * EXT REFS:	UxSpArray	
 * EXT EFFECTS:	UxSpArray 
 * ASSUMPTIONS:
 * REVISIONS:	05/01/93        fix3810 clean-up	
		dd/mm/yy	fix#	fix_description
 *--------------------------------------------------------------------*/
handle UxCreateSubproc(process, defarg, fnt)
     char *process, *defarg;
     void (*fnt)();
{
	SubprocInfo_t	*sp;
	int 		index, i;
	static int 	initialized = 0;
	int 		putenv();


	if (!process || !*process) 
	{
		UxInternalError( __FILE__, __LINE__,
			CGETS( MS_MISC1_NULL_PROC_ERR, DS_MS_MISC1_NULL_PROC_ERR));
		return ERROR;
	}

	if(!initialized)
	{
		for(i = 0; i < MAX_SUBPROC; i++) {
	   		UxSpArray[i] = (SubprocInfo_t *)0;
	   	}
		putenv("TERM=uimx");
		initialized = 1;
	}

	if( (index = UxGetSp()) == ERROR)
		return ERROR;

	sp = UxSpArray[index];
	
	if(! (sp->process = UxMalloc(strlen(process) + 1)) )
	{
		UxInternalError( __FILE__, __LINE__,
			CGETS( MS_MISC_MALLOC_ERR, DS_MS_MISC_MALLOC_ERR));
		UxFree( (char *) sp);
		UxSpArray[index] = (SubprocInfo_t *)0;
		return ERROR;
	}
	strcpy(sp->process, process);

	if(!defarg || (defarg[0] == '\0'))
		sp->defarg = (char *)0;
	else if(! (sp->defarg = UxMalloc(strlen(defarg) + 1)) )
	{
		UxInternalError( __FILE__, __LINE__,
			CGETS( MS_MISC_MALLOC_ERR, DS_MS_MISC_MALLOC_ERR));
		UxFree(sp->process);
		UxFree( (char *) sp);
		UxSpArray[index] = (SubprocInfo_t *)0;
		return ERROR;
	}
	
	if(defarg && (defarg[0] != '\0'))
	   strcpy(sp->defarg, defarg);

	sp->output_fnt = fnt;
	sp->exit_cb = 0;
	sp->echo= 0;

#ifdef	DEBUG
	fprintf(stderr, ">>(%d) [UxCreateSubproc] handle=%d\n", getpid(),
		index);
#endif	/* DEBUG */
	
	return(index);
}

/*----------------------------------------------------------------------
 * NAME:	<int UxSetSubprocEcho(sp_h, echo)>
 * DESCRIPTION:	<Set the echo field for the process>
 * PARAMETERS: 	handle          sp_h - process handle
		int             echo - do we want echo ?
 * RETURN:	ERROR/NO_ERROR	
 * EXT REFS:	UxSpArray	
 * EXT EFFECTS:	UxSpArray	
 * ASSUMPTIONS:
 * REVISIONS:	05/01/93        fix3810 clean-up	
		dd/mm/yy	fix#	fix_description
 *--------------------------------------------------------------------*/
int UxSetSubprocEcho(sp_h, echo)
	handle		sp_h;
	int		echo;
{
	SubprocInfo_t	*sp;

	if(UxCheckIndex(sp_h) == ERROR)
		return(ERROR);

	sp = UxSpArray[sp_h];

	sp->echo= echo;
	return NO_ERROR;
}


/*----------------------------------------------------------------------
 * NAME:	<UxMarkFileCloseOnExec (flag)>
 * DESCRIPTION:	<This function marks all the file descriptors above 2 
                to be closed for the child proc after 'exec' calls.

                This is used before running any standard scripts via system(),
                or subprocesses which involve pipes.

                Otherwise things start hanging if the child process
                cannot 'exec' properly, leading to wierd states on the X
                connection, and the pipes themselves.>
 * PARAMETERS: 	int     flag            -close-on-exec flag
 * RETURN:	NONE	
 * EXT REFS:	globals_used
 * EXT EFFECTS:	globals or other things altered
 * ASSUMPTIONS:
 * REVISIONS:	14/11/91		creation
		04/01/92	fix3384	
		05/01/93	fix3810	cleanup
		07/07/93	fix4339 dg port
		dd/mm/yy	fix#	fix_description
 *--------------------------------------------------------------------*/
void	UxMarkFileCloseOnExec(flag)
int	flag;
{
	int	i;

/* For dg88, we do not want the SYSV code because in design time uimx hangs
   and we have to do Ctrl-C to unhang it.
   It works fine with the 'else' code. 
*/
#if defined(SYSV) && !defined(dg88)
	    for (i = 3; i < _NFILE; i++)
		(void) fcntl(i, F_SETFD, flag);
#else	/* SYSV */
	    for (i = getdtablesize(); i > 2; i--)
		(void) fcntl(i, F_SETFD, flag);
#endif	/* SYSV */
}


/*----------------------------------------------------------------------
 * NAME:	<int UxRunSubproc(sp_vh, cmd_line)>
 * DESCRIPTION:	<Looks up the object and start the subprocess.>
 * PARAMETERS: 	 handle sp_vh -- handle indicating the process
                char *cmd_line -- arguments to the process
 * RETURN:	ERROR / NO_ERROR
 * EXT REFS:	UxSpArray	
 * EXT EFFECTS:	globals or other things altered
 * ASSUMPTIONS:
 * REVISIONS:	05/01/93        fix3810 clean-up	
		dd/mm/yy	fix#	fix_description
 *--------------------------------------------------------------------*/
int 	UxRunSubproc(sp_vh, cmd_line)
handle sp_vh;
char *cmd_line;
{
	char *cmd;
	SubprocInfo_t *sp;

	if(UxCheckIndex(sp_vh) == ERROR) {
                return(ERROR);
        }

	if (UxSpArray[sp_vh]->subprocStatus == DELAYED_EXIT) {
                UxError( CGETS( MS_MISC_HNDL_DELAYED_EXIT,
                                DS_MS_MISC_HNDL_DELAYED_EXIT));
		return(ERROR);
	}

	sp = UxSpArray[sp_vh];

	if(sp->pid > 0) {
		error_msg(CGETS( MS_MISC_SUBPROC_RUNNIN, 
				DS_MS_MISC_SUBPROC_RUNNIN));
		return(ERROR);
	} 

	if(cmd_line && (cmd_line[0] != '\0'))
		cmd = cmd_line;
	else
		cmd = sp->defarg;

	if (UxExecSubprocess(sp, cmd) == ERROR)  {
		return(ERROR);
	}
 
	return(NO_ERROR);
}

/*----------------------------------------------------------------------
 * NAME:	<int UxExecSubproc(sp_vh, cmd_line)>
 * DESCRIPTION:	<Looks up the object and start the subprocess
                Same as UxRunSubproc, but kill the process if it is
                still running. Main purpose is for processes
                that die after execution e.g. "ls".>
 * PARAMETERS: 	handle sp_vh -- handle indicating the process
                char *cmd_line -- arguments to the process
 * RETURN:	ERROR / NO_ERROR	
 * EXT REFS:	globals_used
 * EXT EFFECTS:	globals or other things altered
 * ASSUMPTIONS:
 * REVISIONS:	05/01/93        fix3810 clean-up	
		dd/mm/yy	fix#	fix_description
 *--------------------------------------------------------------------*/
int 	UxExecSubproc(sp_vh, cmd_line)
handle sp_vh;
char *cmd_line;
{
	if(UxExitSubproc(sp_vh) == ERROR)
		return(ERROR);
	if(UxRunSubproc(sp_vh, cmd_line) == ERROR)
		return(ERROR);
	return(NO_ERROR);
}


/*----------------------------------------------------------------------
 * NAME:	< int UxExitSubproc(sp_vh)>
 * DESCRIPTION:	<Remove the input source of the subprocess.
                Kill the process and free the memory associated with
                it.>
 * PARAMETERS:	handle sp_vh -- object handle 
 * RETURN:	ERROR / NO_ERROR
 * EXT REFS:	UxSpArray	
 * EXT EFFECTS:	UxSpArray	
 * ASSUMPTIONS:
 * REVISIONS:	05/01/93        fix3810 clean-up	
		dd/mm/yy	fix#	fix_description
 *--------------------------------------------------------------------*/
int UxExitSubproc(sp_vh)
handle sp_vh;
{
	SubprocInfo_t *sp;

	if (UxCheckIndex(sp_vh) == ERROR) {
		return ERROR;
	}

	sp = UxSpArray[sp_vh];

	if(sp->pid > 0)
	{
		kill(sp->pid, SIGKILL);
	        UxSpExit (sp);
	}

	return(NO_ERROR);
}


/*----------------------------------------------------------------------
 * NAME:	< int UxGetSubprocPid(sp_vh)>
 * DESCRIPTION:	<Test if the process is still alive and return
                pid if it is not. If the process is dead, it
                resets the pid field and closes the pseudo terminal
                fd's if they are open.>
 * PARAMETERS:	handle sp_vh -- object handle 
 * RETURN:	ERROR -1 if the subprocess object does not exist
                0        if the process is dead
                pid      if the process is running	
 * EXT REFS:	UxSpArray	
 * EXT EFFECTS:	UxSpArray	
 * ASSUMPTIONS:
 * REVISIONS:	19/12/91	fix3288	
		05/01/93        fix3810 clean-up
		dd/mm/yy	fix#	fix_description
 *--------------------------------------------------------------------*/
int UxGetSubprocPid(sp_vh)
handle sp_vh;
{
	SubprocInfo_t *sp;

	if(UxCheckIndex(sp_vh) == ERROR)
		return(ERROR);
	sp = UxSpArray[sp_vh];

	if(sp->pid == -1){
		error_msg(CGETS( MS_MISC_SUBPROC_NOACT,
                                DS_MS_MISC_SUBPROC_NOACT));
		return(ERROR);
	}
	/** if there is a process id */
	if(sp->pid > 0)
	{
		/* and the process is dead 
		   -- reset the pid entry
		   -- close the pty/tty and reset the entries
		   -- remove the input source for X
		   */
		if ((kill(sp->pid, 0) == -1) && (errno != EPERM))
		{
			sp->pid = -1;
/*			UxSpExit (sp); */
		}
	}
	return(sp->pid);
}


/*----------------------------------------------------------------------
 * NAME:	<int UxSendSubproc(sp_vh, tex)>
 * DESCRIPTION:	<send a text string to the subprocess 
                 indicated by sp_vh>
 * PARAMETERS	handle *sp_vh - process handle
		char *tex     - string to pass to process
 * RETURN:	ERROR /NO_ERROR	
 * EXT REFS:	globals_used
 * EXT EFFECTS:	globals or other things altered
 * ASSUMPTIONS:
 * REVISIONS:	05/01/93        fix3810 clean-up	
		dd/mm/yy	fix#	fix_description
 *--------------------------------------------------------------------*/
int UxSendSubproc(sp_vh, tex)
handle sp_vh;
char *tex;
{
	char	*tbuff;
	int	ret;

        if(UxCheckIndex(sp_vh) == ERROR)
                return(ERROR);

	if (UxSpArray[sp_vh]->subprocStatus == DELAYED_EXIT) {
                UxError( CGETS( MS_MISC_HNDL_DELAYED_EXIT,
                                DS_MS_MISC_HNDL_DELAYED_EXIT));
		return(ERROR);
	}

	if(tex == NULL) {
	    tex = "";
	}

	tbuff = (char *) UxMalloc((strlen(tex) + 2)*sizeof(char));

	strcpy(tbuff, tex);
	strcat(tbuff, "\n");

	ret = UxSendSubprocNoCR(sp_vh, tbuff);

	UxFree(tbuff);

	return ret;
}


/*----------------------------------------------------------------------
 * NAME:	<int UxSendSubprocNoCR(sp_vh, tex)>
 * DESCRIPTION:	<send a message to the daemon indicating that some input
		is there for a subprocess >
 * PARAMETERS:	handle sp_vh - process handle
		char *tex    - text string
 * RETURN:	ERROR/NO_ERROR	
 * EXT REFS:	UxSpArray	
 * EXT EFFECTS:	globals or other things altered
 * ASSUMPTIONS:
 * REVISIONS:	05/01/93        fix3810 clean-up	
		05/04/93	fix4074	message for errno = EAGAIN
		dd/mm/yy	fix#	fix_description
 *--------------------------------------------------------------------*/
int UxSendSubprocNoCR(sp_vh, tex)
handle sp_vh;
char *tex;
{
	register int len;
	SubprocInfo_t *sp;
	spmd_input_t msg;	/* viewed as input to subprocess */

	if(tex == NULL) {
	    tex = "";
	}

	if(UxCheckIndex(sp_vh) == ERROR)
		return(ERROR);

	if (UxSpArray[sp_vh]->subprocStatus == DELAYED_EXIT) {
                UxError( CGETS( MS_MISC_HNDL_DELAYED_EXIT,
                                DS_MS_MISC_HNDL_DELAYED_EXIT));
		return(ERROR);
	}

	sp = UxSpArray[sp_vh];
	UxSpCheck(sp);

	/* sp_check() sets pid to -1 if process isn't there */
	if(sp->pid == -1) 
	{
		error_msg(CGETS( MS_MISC_SUBPROC_NOACT,
                                DS_MS_MISC_SUBPROC_NOACT));
		return(ERROR);
	} 

	len = strlen(tex);

	msg.mtype = SPMD_INPUT;		/* input from subprocess perspective */
	msg.pid = sp->pid;
	ASSERT (len < sizeof(msg.input)); 	/* Could use more messages.. */
	strcpy (msg.input, tex);
	if (msgsnd(UxSpmd.spmd_pc_msqid, &msg, len+sizeof(msg.pid), IPC_NOWAIT) 
	    == -1)

	{
		if (errno == EAGAIN)
		{
			error_msg(CGETS(MS_MISC1_MSG_NOT_SENT,
                                DS_MS_MISC1_MSG_NOT_SENT));
		}
		else
		{
			
			UxInternalError(__FILE__, __LINE__,
			CGETS( MS_MISC_W_SUBPROC_ERR, DS_MS_MISC_W_SUBPROC_ERR));
			UxSpCheck(sp);
		}
		return(ERROR);
	}
	return(NO_ERROR);
}


/*----------------------------------------------------------------------
 * NAME:	<char * UxTransferToBuffer(fd, status)>
 * DESCRIPTION:	<Transfer data received from the process through the
		daemon to the application>
 * PARAMETERS: 	int fd - not used
		int *status - returns 1 if there is more data
 * RETURN:	char * - pointer to data transferred	
 * EXT REFS:	UxSpmd	
 * EXT EFFECTS:	UxSpmd	
 * ASSUMPTIONS:
 * REVISIONS:	05/01/93        fix3810 clean-up	
		08/01/93	fix3818	to remove r's at end for HP/IBM
		25/03/93	fix4037	deal with multibyte	
 *--------------------------------------------------------------------*/

char * UxTransferToBuffer(fd, status)
int fd, *status;
{
	int len;
	static char text_buff[SIZEOFTEXTMSG];

	if ((UxSpmd.spmd_rmsgrp == NULL) || (UxSpmd.spmd_rmsgsiz <= 0)) {
		return (NULL); /* sanity checking failed */
	}

	/*
	 * Copying could be avoided by returning a pointer into 
	 * UxSpmd.spmd_rmsgrp directly.
	 */
    	len =  UxSpmd.spmd_rmsgsiz;
	strncpy (text_buff, UxSpmd.spmd_rmsgrp, len);
	text_buff[len] = '\0';

	UxSpmd.spmd_rmsgsiz -= len;	/* decrement remainder count */
	UxSpmd.spmd_rmsgrp = NULL;
	*status = 0;	
	return(text_buff);
}


/*----------------------------------------------------------------------
 * NAME:	<void UxAppendTo(fd, tw)>
 * DESCRIPTION:	<Output data received by application into widget>
 * PARAMETERS:	int fd - not used
		Widget  tw  - widget to output data to
 * RETURN:	void	
 * EXT REFS:	globals_used
 * EXT EFFECTS:	globals or other things altered
 * ASSUMPTIONS:
 * REVISIONS:	05/01/93        fix3810 clean-up	
		dd/mm/yy	fix#	fix_description
 *--------------------------------------------------------------------*/
void UxAppendTo(fd, tw)
int fd;
Widget  tw;
{
	char *text;
	int more;

	while( (text = UxTransferToBuffer(fd, &more)) )
	{
		if(tw)
			UxTextAppend(tw, text);
		else 
			fprintf(stdout, "%s",text);

		if(!more)
			break;
	}
}
    

/*----------------------------------------------------------------------
 * NAME:	<int UxSetSubprocClosure(sp_vh, data)>
 * DESCRIPTION:	<Sets the data/closure passed to the output function>
 * PARAMETERS:	handle          sp_vh           - handle to subproc
                char            *data           - data passed to the
						  subprocess function
 * RETURN:	ERROR/NO_ERROR	
 * EXT REFS:	UxSpArray	
 * EXT EFFECTS:	UxSpArray	
 * ASSUMPTIONS:
 * REVISIONS:	05/01/93        fix3810 clean-up	
		dd/mm/yy	fix#	fix_description
 *--------------------------------------------------------------------*/
int UxSetSubprocClosure(sp_vh, data)
handle sp_vh;
char *data;
{
	SubprocInfo_t *sp;

	if(UxCheckIndex(sp_vh) == ERROR)
		return(ERROR);
	sp = UxSpArray[sp_vh];
  
	sp->user_data = data;
	return(NO_ERROR);
}


/*----------------------------------------------------------------------
 * NAME:	<int             UxSetSubprocFunction(sp_vh, fnt)>
 * DESCRIPTION:	<Sets the output function that is executed on ouput
                from the child process>
 * PARAMETERS:	handle          sp_vh           - handle to subproc
                void            (*fnt)()        - output function
 * RETURN:	ERROR/NO_ERROR	
 * EXT REFS:	UxSpArray	
 * EXT EFFECTS:	UxSpArray	
 * ASSUMPTIONS:
 * REVISIONS:	05/01/93        fix3810 clean-up	
		dd/mm/yy	fix#	fix_description
 *--------------------------------------------------------------------*/
int UxSetSubprocFunction(sp_vh, fnt)
handle sp_vh;
void (*fnt)();
{
	SubprocInfo_t *sp;

	if (UxCheckIndex(sp_vh) == ERROR)
		return(ERROR);

	sp = UxSpArray[sp_vh];
	sp->output_fnt = fnt;
	return(NO_ERROR);
}
