/*---------------------------------------------------------------------
 * $Date: 2009-08-18 15:23:20 $		$Revision: 1.4 $
 *---------------------------------------------------------------------
 *             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.

.VERSION
 090813		last modif KB

 *---------------------------------------------------------------------
 *   File_Description_Section
 *--------------------------------------------------------------------*/

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

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


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

/*--- forward declaration of static functions ---*/
static void InitializeSubprocMgr UXPROTO((void));


/*----------------------------------------------------------------------
 * NAME:	<static void InitializeSubprocMgr()>
 * DESCRIPTION:	<Initialize struct SubprocMgr_t for functions to run
		according to type of subproc: streams or ptys  >
 * PARAMETERS: 	NONE     
 * RETURN:	void	
 * EXT REFS:	globals used	
 * EXT EFFECTS:	globals or other things altered
 * ASSUMPTIONS: the fields of the structure are accessed by #define
 * REVISIONS:	04/01/93	fix3810	creation	
		dd/mm/yy	fix#	fix_description
 *--------------------------------------------------------------------*/
static void InitializeSubprocMgr()

{
#ifdef _PTY_DRIVER
	UX_PREPARE_COMMAND = UxPtyPrepareCommand;
	UX_SET_PARENT_SIDE = UxGetPtty;
	UX_SETUP_PARENT_COMMUNICATION = UxSetPseudo;
	UX_START_CHILD = UxPtyStartChild;
	UX_SET_CHILD_SIDE = UxSetTty;
	UX_SETUP_CHILD_COMMUNICATION = UxPtyChildCommSetup;
	UX_SPMD_EXEC_SUBPROCESS = UxPtyExecSubprocess;
	UX_CLOSE_COMMUNICATION = UxPtyExitHandler;
#endif

#ifdef _STREAM_DRIVER
	UX_PREPARE_COMMAND = UxStreamPrepareCommand;
	UX_SET_PARENT_SIDE = UxGetStream;
	UX_SETUP_PARENT_COMMUNICATION = NULL;
	UX_START_CHILD = UxStreamStartChild;
	UX_SET_CHILD_SIDE = UxSetStream;
	UX_SETUP_CHILD_COMMUNICATION = UxStreamChildCommSetup;
	UX_EXEC_SUBPROCESS = UxStreamExecSubprocess;
	UX_CLOSE_COMMUNICATION = UxStreamExitHandler;
#endif
}


/*----------------------------------------------------------------------
 * NAME:	<int UxGetSp()>
 * DESCRIPTION:	<initialization of a newly created object>
 * PARAMETERS:	NONE 
 * RETURN:	int 	- index of new process in processes array	
 * EXT REFS:	UxSpmd, UxSpArray	
 * EXT EFFECTS:	UxSpArray	
 * ASSUMPTIONS:
 * REVISIONS:	05/01/93        fix3810 clean-up
		dd/mm/yy	fix#	fix_description
 *--------------------------------------------------------------------*/
int UxGetSp()

{
	/* declare register as int because of PS/2 compiler 		*/
	/* where the default register declarations are of type const 	*/
	register int i; 

	InitializeSubprocMgr();

	/* Start the Sub-Process Manager Daemon 
	 * if it hasn't already been started.
	 */
	if ((UxSpmd.spmd_pid <= 0) && (UxSpmdCreateDaemon() == ERROR)) {
		error_msg("Subprocess Manager Daemon cannot be started");
		return(ERROR);
	}


	/* look for a free slot in the array */
	for(i = 0; i < MAX_SUBPROC; i++)
	{
		if( UxSpArray[i] == (SubprocInfo_t *)0)
			break;
	}
	if(i == MAX_SUBPROC)
	{
		char *msg, *fmt;

		fmt = CGETS( MS_MISC_MAX_PROCS, DS_MS_MISC_MAX_PROCS);
		msg = UxMalloc(strlen(fmt) + 16 + 1);
		sprintf(msg,fmt,MAX_SUBPROC);
		error_msg(msg);
		UxFree(msg);
		return(ERROR);
	}
	else
	{
		if(! (UxSpArray[i] = (SubprocInfo_t *)UxCalloc(1, sizeof(SubprocInfo_t))) )
		{
			UxInternalError(__FILE__, __LINE__,
			    CGETS( MS_MISC_CALLOC_ERR,DS_MS_MISC_CALLOC_ERR));
		        return ERROR;
                }
                else 
		{
			UxSpArray[i]->handle = -1;
			UxSpArray[i]->pid = -1;
			UxSpArray[i]->subprocStatus = UNDEFINED;
			return i;
		}
        }
} 


/*----------------------------------------------------------------------
 * NAME:	<int    UxCheckIndex(index)>
 * DESCRIPTION:	<returns ERROR if invalid handle or entry not set
                and NO_ERROR if valid>
 * PARAMETERS:	handle          index           - index to subproc struct 
 * RETURN:	ERROR/NO_ERROR
 * EXT REFS:	UxSpArray	
 * EXT EFFECTS:	globals or other things altered
 * ASSUMPTIONS:
 * REVISIONS:	05/01/93        fix3810 clean-up	
		11/03/93	fix3989 tested for MAX_SUBPROC - 1
		dd/mm/yy	fix#	fix_description
 *--------------------------------------------------------------------*/
int UxCheckIndex(index)
handle index;

{
	if ((index < 0) || (index > (MAX_SUBPROC - 1))) {
		UxError( CGETS( MS_MISC_INV_HNDL, DS_MS_MISC_INV_HNDL));
		return(ERROR);
	}

	if( !(UxSpArray[index]) ) {
		UxError( CGETS( MS_MISC_NOT_HNDL, DS_MS_MISC_NOT_HNDL));
		return(ERROR);
	}

	return(NO_ERROR);
}


/*----------------------------------------------------------------------
 * NAME:	<void UxSyserr(msg1, msg2)>
 * DESCRIPTION:	<Generate an error message including the system errno.>
 * PARAMETERS: 	char *msg1, *msg2; message to print.
 * RETURN:	void	
 * EXT REFS:	globals_used
 * EXT EFFECTS:	globals or other things altered
 * ASSUMPTIONS:
 * REVISIONS:	05/01/93	fix3810	cleanup	
 *		dd/mm/yy	fix#	fix_description
 *--------------------------------------------------------------------*/
void UxSyserr(msg1, msg2)
char *msg1;
char *msg2;

{
int error;

extern int errno;

char buffer[BUFSIZ];

    *buffer = '\0';
    error = errno;

    if (msg1 && *msg1) {
	(void) strcat(buffer, msg1);
	(void) strcat(buffer, ": ");
	if (msg2 && *msg2) {
	    (void) strcat(buffer, msg2);
	    (void) strcat(buffer, ": ");
	}
    }
    (void) strcat(buffer, strerror(errno));
    (void) strcat(buffer, "\n");
    UxStandardError(buffer);
}


/*----------------------------------------------------------------------
 * NAME:	<void UxOnExitRunExitCb(pid, status)>
 * DESCRIPTION:	<For the specified process, run the exit callback function
		and then remove the process>
 * PARAMETERS:	int pid - process id
		int status - status of the process
 * RETURN:	void	
 * EXT REFS:	UxSpArray	
 * EXT EFFECTS:	UxSpArray	
 * ASSUMPTIONS:
 * REVISIONS:	05/01/93        fix3810 clean-up	
		02/04/93	fix4074	set subprocStatus flag
		dd/mm/yy	fix#	fix_description
 *--------------------------------------------------------------------*/
void UxOnExitRunExitCb(pid, status)
int pid;
int status;

{
int i;

	if (pid != -1)
	{
		for (i=0; i<MAX_SUBPROC; i++)
		{
			if (!UxSpArray[i])
				continue;
			if (UxSpArray[i]->pid == pid)
			{
				UxSpArray[i]->subprocStatus = EXITING; 
				if (UxSpArray[i]->exit_cb) {
				       (*UxSpArray[i]->exit_cb)(pid, status, i);
				}
				UxSpExit(UxSpArray[i]);
				if (UxSpArray[i]->subprocStatus!=DELAYED_EXIT) {
					UxSpArray[i]->pid = -1;
					UxSpArray[i]->subprocStatus = UNDEFINED;
				}
				break;
			}
	    }
	}
}


/*----------------------------------------------------------------------
 * NAME:	<char    **UxBuildArgs(process, command_line)>
 * DESCRIPTION:	<Small parser of the argument list.  It supports the
                following:
			- '\' quotes the next character.
			- all text inside of "" is quoted (with
			  the exception of '\' quoting).
			- all text inside of '' is quoted (with
			  the exception of '\' quoting).
			- arguments are separated by non quoted spaces
			  or tabs.
			- there is no attempt to remove special characters
			  since they may mean something to the application
			  which is running the command (such as:
			  sh -c "filter </etc/passwd").
			- dynamically creates the arg list.
			- dynamically creates storage for breaking up
			  command line. >
 * PARAMETERS:	char *process, *command_line -- args 
 * RETURN:	 char **args - returns pointer to the argument array	
 * 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
 *--------------------------------------------------------------------*/
char 	**UxBuildArgs(process, command_line)
char 	*process, *command_line;

{
static char *Buffer = NULL;
static char **Argv = (char **) 0;
register char *From;
register char *To;
register char **ArgvPtr;
register int Quote;
int i;

    if (! command_line)
        command_line = "";

    /* allocate storage for string to be mucked with and copy it over... */
    if (Buffer)
	Buffer = (char*)UxRealloc(Buffer, strlen(command_line) + 1);
    else
	Buffer = UxMalloc(strlen(command_line) + 1);
    (void) strcpy(Buffer, command_line);

    /* Allocate storage for Argv.  We don't need to be exact.  We just don't
     * want to underestimate the number of arguments (or we trash memory),
     * and we don't want to grossly overestimate the amount of memory.  If
     * we count all groups of spaces as separators we will be close -- we
     * will overcount any that are quoted.
     */
    /* The first one is for the command being executed... */
    for (i = 1, From = Buffer; *From; ) {
	/* count this as a potential argument... */
	(void) i++;

	/* skip over characters... */
	while (*From && ((*From != ' ') && (*From != '\t')))
	    (void) From++;
	
	/* skip over white space... */
	while (*From && ((*From == ' ') || (*From == '\t')))
	    (void) From++;
    }

    /* leave an extra one for null terminating the array... */
    if (Argv)
	Argv = (char **) UxRealloc((char *) Argv, (i + 1) * sizeof(char *));
    else
	Argv = (char **) UxMalloc((i + 1) * sizeof(char *));
	
    /* initialize the world... */
    ArgvPtr = Argv;
    From = Buffer;
    To = Buffer;
    Quote = 0;

    *ArgvPtr++ = process;
    *ArgvPtr = To;

    /*
    ** skip over leading blanks
    */

    while (*From && ((*From == ' ') || (*From == '\t')))
	(void) From++;

    if (*From == '\0') {
	*ArgvPtr = (char *) 0;
	return (Argv);
    }
    
    while (*From) {
#ifdef DEBUGPARSE
	printf ("char='%c' ", *From);
#endif /* DEBUGPARSE */

        switch (*From) {
	    case '\'' :
	    case '"' :
		/*
		** if we are not quoting, start doing so
		*/

		if (Quote == 0) {
#ifdef DEBUGPARSE
		    printf ("quoting on\n");
#endif /* DEBUGPARSE */

		    Quote = *From++;
		    break;
		}

		/*
		** if we are done quoting, do so
		*/

		if (Quote == *From) {
#ifdef DEBUGPARSE
		    printf ("quoting off\n");
#endif /* DEBUGPARSE */

		    Quote = 0;
		    From++;
		    break;
		}

		/*
		** else, it is a valid character
		*/

#ifdef DEBUGPARSE
		printf ("copied\n");
#endif /* DEBUGPARSE */

		*To++ = *From++;
		break;

	    case ' ' :
	    case '\t' :
		if (Quote) {
#ifdef DEBUGPARSE
		    printf ("quoted tab/space\n");
#endif /* DEBUGPARSE */

		    *To++ = *From++;
		    break;
		}

		/*
		** put in a null and advance ArgvPtr
		*/

#ifdef DEBUGPARSE
		printf ("seperator\n");
#endif /* DEBUGPARSE */

		while ((*From == ' ') || (*From == '\t'))
		    (void) From++;

		*To++ = '\0';

		/* if we are done, return the string.  This takes care
		 * of the problem of command lines terminated by spaces.
		 */
		if (*From == '\0') {
		    *(++ArgvPtr) = (char *) 0;
		    return (Argv);
		}

		*(++ArgvPtr) = To;
		break;

	    case '\\' :
		/*
		** quote the next character directly
		*/

		(void) From++;
		if (*From) {
#ifdef DEBUGPARSE
		    printf ("quoting '%c'\n", *From);
#endif /* DEBUGPARSE */

		    *To++ = *From++;
		}
		break;

	    default :
#ifdef DEBUGPARSE
		printf ("copied\n");
#endif /* DEBUGPARSE */

		*To++ = *From++;
		break;
        }
    }

    *To = '\0';
    *(++ArgvPtr) = (char *) 0;
return (Argv);
}

#ifdef DEBUG
/*----------------------------------------------------------------------
 * NAME:	<int UxPrintArgs(tmp)>
 * DESCRIPTION:	<debug fnt - print the argument list>
 * PARAMETERS: 	 char **tmp - arg list
 * RETURN:	NONE	
 * 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 UxPrintArgs(tmp)
  char **tmp;
{
  int i;
  
  printf("Argument list\n");
  for(i = 0; tmp[i]; i++)
    printf("Arg %d: %s\n", i, tmp[i]);
  printf("\n");
}
#endif /* DEBUG */


/*----------------------------------------------------------------------
 * NAME:	<void    UxSpExit (sp)>
 * DESCRIPTION:	<Closes the pty/tty pair or the stream (if open),
                removes the input source for X, and resets the 'input_id'.>
 * PARAMETERS:	subproc *sp - process that exits
 * RETURN:	void	
 * EXT REFS:	globals_used
 * EXT EFFECTS:	UxSpArray via sp	
 * ASSUMPTIONS:
 * REVISIONS:	13/06/89		creation	
		05/01/93        fix3810 clean-up
		dd/mm/yy	fix#	fix_description
 *--------------------------------------------------------------------*/

void	UxSpExit (sp)
SubprocInfo_t	*sp;

{
	if(sp->handle != -1)
	{
		(void)close(sp->handle);
		sp->handle = -1;
	}
}


/*----------------------------------------------------------------------
 * NAME:	<void UxSpCheck(sp)>
 * DESCRIPTION:	<check if the subprocess is still alive>
 * PARAMETERS:	subproc *sp -- subprocess object 
 * RETURN:	void	
 * EXT REFS:	globals_used
 * EXT EFFECTS:	UxSpArray via sp	
 * ASSUMPTIONS:
 * REVISIONS:	19/12/91	fix3288	
		05/01/93        fix3810 clean-up
		dd/mm/yy	fix#	fix_description
 *--------------------------------------------------------------------*/
void UxSpCheck(sp)
     SubprocInfo_t *sp;

{
	if(!sp)
		return;

	if ((kill(sp->pid, 0) == -1) && (errno != EPERM))
	{
		sp->pid = -1;
		UxSpExit (sp);
	}
}


/*----------------------------------------------------------------------
 * NAME:	<void UxOutputHandler(sp)>
 * DESCRIPTION:	<Low level output function - reads the output
                of the subprocess into a temp. buffer and executes
                the user function passing a pointer to the buffer.
                NOTE: This might be changed to just passing the fd
                and a pointer to user supplied data. >
 * PARAMETERS:	subproc *sp - subproc object 
 * RETURN:	void	
 * EXT REFS:	globals_used
 * EXT EFFECTS:	globals or other things altered
 * ASSUMPTIONS:
 * REVISIONS:	05/01/93	fix3810	cleanup
 *		dd/mm/yy	fix#	fix_description
 *--------------------------------------------------------------------*/
void UxOutputHandler(sp)
     SubprocInfo_t *sp;

{
	if(!sp)
	{
		error_msg(CGETS( MS_MISC_INV_SPRC_STRUCT,
                                DS_MS_MISC_INV_SPRC_STRUCT));
		return;
	}

	if(! sp->output_fnt )
	{
		error_msg(CGETS( MS_MISC_INV_OUT_FNCTN,
                                DS_MS_MISC_INV_OUT_FNCTN));
		return;
	}

	(*sp->output_fnt) (sp->handle, sp->user_data);
}

/*----------------------------------------------------------------------
 * NAME:	<int             UxKillAllSubprocs()>
 * DESCRIPTION:	<kills any remaining subprocess>
 * PARAMETERS: 	none 
 * RETURN:	nothing	
 * EXT REFS:	UxSpArray	
 * EXT EFFECTS:	UxSpArray	
 * ASSUMPTIONS:
 * REVISIONS:	23/04/93        fix4181 handle Ctrl-C in design time	
		dd/mm/yy	fix#	fix_description
 *--------------------------------------------------------------------*/
void UxKillAllSubprocs()

{
	int i;

	for(i = 0; i < MAX_SUBPROC; i++) {
	   	if( (UxSpArray[i] != (SubprocInfo_t *)0) &&
		   (UxSpArray[i]->subprocStatus !=  EXITING)) {
			UxDeleteSubproc (i);
	   	}
	}
}
