/*---------------------------------------------------------------------
 * $Date: 2009-08-18 15:23:20 $		$Revision: 1.6 $
 *---------------------------------------------------------------------
 *             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
 090818		last modif

*/



#if defined(__linux__)
#include <termios.h>

#if defined(__alpha__)

#ifndef _ALPHA_TERMIOS_H
struct ltchars {
        char    t_suspc;
        char    t_dsuspc;
        char    t_rprntc;
        char    t_flushc;
        char    t_werasc;
        char    t_lnextc;
};
#endif
#endif
#endif

#if defined (sgi)
#include <sys/ttold.h>
#endif

#if defined(__FreeBSD__) || defined(__Darwin__)
#define USE_OLD_TTY	/*need to get B9600 definition in <sys/ttydev.h>*/
#include <sys/ttycom.h>
#include <sys/filio.h>
#include <sys/ttydev.h>
#include <sys/ttydefaults.h>
#include <sys/ioctl_compat.h>
#endif

#include <stdlib.h>
#include <unistd.h>


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



extern void  UxMarkFileCloseOnExec();


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


/* The following structures are initialized in main() in order
** to eliminate any assumptions about the internal order of their
** contents.
*/
#ifdef USE_SYSV_TERMIO
                struct termio d_tio;
                struct termio tio;
#ifdef TIOCSLTC
                        struct ltchars d_ltc;
                        struct ltchars ltc;
#endif	/* TIOCSLTC */


#ifdef TIOCLSET
                        unsigned int d_lmode;
                        unsigned int lmode;
#endif	/* TIOCLSET */
#else		/* not USE_SYSV_TERMIO */
                struct  sgttyb d_sg = {
                        0, 0, 0177, CKILL, EVENP|ODDP|ECHO|XTABS|CRMOD
                        };
                struct  sgttyb sg;

                struct  tchars d_tc = {
                        CINTR, CQUIT, CSTART,
                        CSTOP, CEOF, CBRK,
                        };
                struct  tchars tc;

                struct  ltchars d_ltc = {
                        CSUSP, CDSUSP, CRPRNT,
                        CFLUSH, CWERASE, CLNEXT
                        };
                struct  ltchars ltc;

                int d_discipline = NTTYDISC;
                int discipline;

                long int d_lmode = LCRTBS|LCRTERA|LCRTKIL|LCTLECH;
                long int lmode;
#endif /* USE_SYSV_TERMIO */


#ifndef HP
char PTY_dev[]          = "/dev";
char PTY_0_9a_f[]       = "0123456789abcdef";
char PTY_p_r[]          = "pqr";
#define PTY_null                (char *) 0

struct _pty_dirs pty_dirs[] = {
    {PTY_dev,     PTY_dev,      PTY_p_r,    PTY_0_9a_f, PTY_null, False},
    {PTY_null,    PTY_null,     PTY_null,   PTY_null,   PTY_null, False},
};

#else   /* HP */
char PTY_dev[]          = "/dev";
char PTY_dev_pty[]      = "/dev/pty";
char PTY_dev_ptym[]     = "/dev/ptym";
char PTY_a_ce_o[]       = "abcefghijklmno";
char PTY_0_9[]          = "0123456789";
char PTY_0_9a_f[]       = "0123456789abcdef";
char PTY_p_z[]          = "pqrstuvwxyz";
char PTY_p_r[]          = "pqr";
#define         PTY_null        (char *) 0

struct _pty_dirs pty_dirs[] = {
    {PTY_dev_pty, PTY_dev_ptym, PTY_a_ce_o, PTY_0_9,    PTY_0_9,  True},
    {PTY_dev_pty, PTY_dev_ptym, PTY_p_z,    PTY_0_9,    PTY_0_9,  True},
    {PTY_dev_pty, PTY_dev_ptym, PTY_a_ce_o, PTY_0_9a_f, PTY_null, True},
    {PTY_dev_pty, PTY_dev_ptym, PTY_p_z,    PTY_0_9a_f, PTY_null, False},
    {PTY_dev,     PTY_dev,      PTY_p_r,    PTY_0_9a_f, PTY_null, False},
    {PTY_null,    PTY_null,     PTY_null,   PTY_null,   PTY_null, False},
};
#endif  /* HP */

char *ttydev = (char *) 0;
char *ptydev = (char *) 0;

int pc_pipe[2];         /* parent to child communication...     */
int cp_pipe[2];         /* child to parent communication...     */

jmp_buf env;

/*--- forward declaration of static functions ---*/
static void hungtty UXPROTO((int));
static void pass_syserr UXPROTO((char *msg1, char *msg2));

/*----------------------------------------------------------------------
 * NAME:	<void UxPtyExitHandler()>
 * DESCRIPTION:	<Find out process that died and run whatever necessary
		before exiting >
 * PARAMETERS:	none
 * RETURN:	void	
 * EXT REFS:	UxSpmd	
 * EXT EFFECTS:	globals or other things altered
 * ASSUMPTIONS:
 * REVISIONS:	05/01/93	fix3810	clean-up	
		dd/mm/yy	fix#	fix_description
 *--------------------------------------------------------------------*/
void UxPtyExitHandler()
{
int	status, pid;

pid = UxWhatSpIsDead(&status);


UxOnExitRunExitCb(pid, status);
return;
}



/*----------------------------------------------------------------------
 * NAME:	<static hungtty()>
 * DESCRIPTION:	<description>
 * PARAMETERS: 	none
 * RETURN:	none	
 * EXT REFS:	env	
 * EXT EFFECTS:	globals or other things altered
 * ASSUMPTIONS:
 * REVISIONS:	05/01/93       	fix3810	clean-up
		dd/mm/yy	fix#	fix_description
 *--------------------------------------------------------------------*/
static void hungtty(sig)
int sig;
{
longjmp(env, 1);
}


/*----------------------------------------------------------------------
 * NAME:	<int UxGetPtty(pty, reset)>
 * DESCRIPTION:	< Find an available pty/tty line and open it,
                setting the passed variable.  This function is based on the
		X11 xterm and hpterm get_pty routine.  It does not open the
		pty slave because it must be opened in the child process
		in order to prevent possible process group problems.  For
		this reason, it is designed to be reentrant to allow it
		to be called repeatedly in the event that the child process
		is unable to open the child pty.

		It is also written to be easily ported to different
		architectures that support different pty naming schemes.
 
 		The master tty file device name is left in global ptydev.
 		The slave tty file device name is left in global ttydev.
 
 		A separate block (currently used only for sgi)
 		handles the case where one master device is always opened,
 		and the minor number of the result identifies the slave.
 * PARAMETERS:	int *pty;
		int reset;
 * RETURN:	ERROR/NO_ERROR	
 * EXT REFS:	globals_used
 * EXT EFFECTS:	globals or other things altered
 * ASSUMPTIONS:
 * REVISIONS:	10/10/90	fix1085	IRIX support added	
		05/01/93       	fix3810	clean-up
		11/03/93	fix3989 use multiplexed special file for pty
		dd/mm/yy	fix#	fix_description
 *--------------------------------------------------------------------*/
int UxGetPtty(pty, reset)
int *pty;
int reset;
{
static struct _pty_dirs *pty_dirs_ptr;
static char *char_1;
static char *char_2;
static char *char_3;
static int first = 1;
static int initialize = 1;
int tty;

if (reset || initialize) {
	/* set up pointers into pty filename structures stuff... */
	pty_dirs_ptr = pty_dirs;
	char_1 = pty_dirs_ptr->char_1;
	char_2 = pty_dirs_ptr->char_2;
	char_3 = pty_dirs_ptr->char_3;
    }

    if (initialize) {
	/* initialize terminal modes... */

#ifdef USE_SYSV_TERMIO
	/* Initialization is done here rather than above in order
	** to prevent any assumptions about the order of the contents
	** of the various terminal structures (which may change from
	** implementation to implementation).
	*/
#ifdef macII
	d_tio.c_iflag = ICRNL|IXON;
	d_tio.c_oflag = OPOST|ONLCR|TAB3;
    	d_tio.c_cflag = B9600|CS8|CREAD|PARENB|HUPCL;
    	d_tio.c_lflag = ISIG|ICANON|ECHO|ECHOE|ECHOK;

	d_tio.c_line = 0;

	d_tio.c_cc[VINTR] = CINTR;
	d_tio.c_cc[VQUIT] = CQUIT;
	d_tio.c_cc[VERASE] = CERASE;
	d_tio.c_cc[VKILL] = CKILL;
    	d_tio.c_cc[VEOF] = CEOF;
	d_tio.c_cc[VEOL] = CNUL;
	d_tio.c_cc[VEOL2] = CNUL;
	d_tio.c_cc[VSWTCH] = CNUL;

        d_ltc.t_suspc = CSUSP;		/* t_suspc */
        d_ltc.t_dsuspc = CDSUSP;	/* t_dsuspc */
        d_ltc.t_rprntc = 0;		/* reserved...*/
        d_ltc.t_flushc = 0;
        d_ltc.t_werasc = 0;
        d_ltc.t_lnextc = 0;
#else  /* macII */

	d_tio.c_iflag = ICRNL|IXON|IXOFF;
	d_tio.c_oflag = OPOST|ONLCR|TAB3;

#ifdef BAUD_0
    	d_tio.c_cflag = CS8|CREAD|PARENB|HUPCL;
	/*  Needed to turn on pacing from the pty to _subproc.c.  
	 *  This will cause the program generating output to block 
	 *  if the pty input buffer fills up.
	 */
#else	/* !BAUD_0 */

    	d_tio.c_cflag = B9600|CS8|CREAD|PARENB|HUPCL;
#endif	/* !BAUD_0 */

    	d_tio.c_lflag = ISIG|ICANON|ECHO|ECHOE|ECHOK;
	d_tio.c_line = 0;
	d_tio.c_cc[VINTR] = 0x7f;		/* DEL  */
	d_tio.c_cc[VQUIT] = '\\' & 0x3f;	/* '^\'	*/
	d_tio.c_cc[VERASE] = '#';		/* '#'	*/
	d_tio.c_cc[VKILL] = '@';		/* '@'	*/
    	d_tio.c_cc[VEOF] = 'D' & 0x3f;		/* '^D'	*/
	d_tio.c_cc[VEOL] = '@' & 0x3f;		/* '^@'	*/

#ifdef VSWTCH
	d_tio.c_cc[VSWTCH] = '@' & 0x3f;	/* '^@'	*/
#endif	/* VSWTCH */

/* now, try to inherit tty settings */
	{
	    int i;

	    for (i = 0; i <= 2; i++) {
		struct termio deftio;
		if (ioctl (i, TCGETA, &deftio) == 0) {
		    d_tio.c_cc[VINTR] = deftio.c_cc[VINTR];
		    d_tio.c_cc[VQUIT] = deftio.c_cc[VQUIT];
		    d_tio.c_cc[VERASE] = deftio.c_cc[VERASE];
		    d_tio.c_cc[VKILL] = deftio.c_cc[VKILL];
		    d_tio.c_cc[VEOF] = deftio.c_cc[VEOF];
		    d_tio.c_cc[VEOL] = deftio.c_cc[VEOL];
#ifdef VSWTCH
		    d_tio.c_cc[VSWTCH] = deftio.c_cc[VSWTCH];
#endif /* VSWTCH */
		    break;
		}
	    }
	}

#ifdef TIOCSLTC
        d_ltc.t_suspc = '\000';		/* t_suspc */
        d_ltc.t_dsuspc = '\000';	/* t_dsuspc */
        d_ltc.t_rprntc = '\377';	/* reserved...*/
        d_ltc.t_flushc = '\377';
        d_ltc.t_werasc = '\377';
        d_ltc.t_lnextc = '\377';
#endif	/* TIOCSLTC */

#ifdef TIOCLSET
	d_lmode = 0;
#endif	/* TIOCLSET */
#endif  /* macII */
#endif	/* USE_SYSV_TERMIO */

	initialize = 0;

	/* Try to inherit modes from /dev/tty...  */

	/*
	 * Sometimes /dev/tty hangs on open (as in the case of a pty
	 * that has gone away).  Simply make up some reasonable
	 * defaults.
	 */

     {  void (*save_func)() =
	signal(SIGALRM, hungtty);
	alarm(2);		/* alarm(1) might return too soon */

	if (! setjmp(env)) {
	    tty = open ("/dev/tty", O_RDWR, 0);
	    alarm(0);
	} else {
	    tty = -1;
	    errno = ENXIO;
	}
	signal(SIGALRM, save_func);
	/*
	 * We should probably save/restore the alarm() value also,
	 * in case the application is using alarms and using GetPtty().
	 */
     }

	if (tty < 0) {

#ifdef USE_SYSV_TERMIO
	    tio = d_tio;
#ifdef TIOCSLTC
	    ltc = d_ltc;
#endif	/* TIOCSLTC */

#ifdef TIOCLSET
	    lmode = d_lmode;
#endif	/* TIOCLSET */

#else	/* not USE_SYSV_TERMIO */
	    sg = d_sg;
	    tc = d_tc;
	    discipline = d_discipline;
	    ltc = d_ltc;
	    lmode = d_lmode;
#endif	/* USE_SYSV_TERMIO */

	} else {
	    /* Get a copy of the current terminal's state...
	     */


#ifdef USE_SYSV_TERMIO
	    if (ioctl(tty, TCGETA, &tio) == -1)
		tio = d_tio; /* should probably print an error message... */
#ifdef TIOCSLTC
	    if (ioctl(tty, TIOCGLTC, &ltc) == -1)
		ltc = d_ltc; /* should probably print an error message... */
#endif	/* TIOCSLTC */

#ifdef TIOCLSET
	    if (ioctl(tty, TIOCLGET, &lmode) == -1)
		lmode = d_lmode; /* should probably print an error message... */
#endif	/* TIOCLSET */

#else	/* not USE_SYSV_TERMIO */
	    if (ioctl(tty, TIOCGETP, (char *)&sg) == -1)
		sg = d_sg; /* should probably print an error message... */
	    if (ioctl(tty, TIOCGETC, (char *)&tc) == -1)
		tc = d_tc;; /* should probably print an error message... */
	    if (ioctl(tty, TIOCGETD, (char *)&discipline) == -1)
		/* should probably print an error message... */
		discipline = d_discipline;
	    if (ioctl(tty, TIOCGLTC, (char *)&ltc) == -1)
		ltc = d_ltc;; /* should probably print an error message... */
	    if (ioctl(tty, TIOCLGET, (char *)&lmode) == -1)
		lmode = d_lmode; /* should probably print an error message... */
#endif	/* USE_SYSV_TERMIO */

	    (void) close (tty);
	}
    }

    /* Find a master/slave device pair. */

#if defined(sgi)

	/* On IRIX, 
	 * master/slave pty pairs are found by opening /dev/ptc as master.
	 * The resulting minor number N identifies the slave /dev/ttyqN.
	 */
	ttydev = (char *) 
		UxCopyString( _getpty( pty, O_RDWR|O_NDELAY, 0666, 0));

#elif defined(ibmr2)
	/* On ibm, pty and tty names are /dev/ptc/xx and /dev/pts/xx where xx
	 * is the channel number.
	 * Master/slave pty pairs are found by opening /dev/ptc as master.
	 * The slave name (/dev/pts) is obtained using ttyname().  
	 */
        *pty = open("/dev/ptc", O_RDWR, 0);
        ttydev = UxCopyString(ttyname(*pty));
#else /* not sgi  or ibmr2 */

    /* On HP (and many other machines) pty master/slaves
     * are opened as two devices with parallel names,
     * such as /dev/ptym/pty38 and /dev/pty/tty38
     */

    /* as long as these are valid, we have a good pair... */
    while (pty_dirs_ptr->pty_dir && pty_dirs_ptr->ptym_dir) {
	/* allocate space for the pty slave... */
	if (ttydev)
	    (void) UxFree(ttydev);
	ttydev = UxMalloc((unsigned) strlen(pty_dirs_ptr->pty_dir) + 8);

	/* allocate space for the pty master... */
	if (ptydev)
	    (void) UxFree(ptydev);
	ptydev = UxMalloc((unsigned) strlen(pty_dirs_ptr->ptym_dir) + 8);

	/* check malloc success... */
	if (!ttydev || !ptydev) {
	    UxInternalError(__FILE__, __LINE__,
		CGETS( MS_MISC_MALLOC_ERR, DS_MS_MISC_MALLOC_ERR));
	    return(ERROR);
	}

	while ((first || !pty_dirs_ptr->fast) && *char_1) {
	    while ((first || !pty_dirs_ptr->fast) && *char_2) {
		if ((first || !pty_dirs_ptr->fast) && char_3) {
		    while ((first || !pty_dirs_ptr->fast) && *char_3) {
			(void) sprintf(ttydev, "%s/tty%c%c%c",
				pty_dirs_ptr->pty_dir,
				*char_1, *char_2, *char_3);
			(void) sprintf(ptydev, "%s/pty%c%c%c",
				pty_dirs_ptr->ptym_dir,
				*char_1, *char_2, *char_3);
			if ((*pty = open(ptydev, O_RDWR)) >= 0) {
			    /* we need to set things up for our next entry
			     * into this function...
			     */
			    (void) char_3++;
			    /* success... */
#ifdef	HP
			    /*  We need to set the modes here.  If we wait until
			     *  after the program is writing to the pty slave 
			     *  before we do this we end up with some of the
			     *  output getting flushed on us.  It tends to be 
			     *  visible as the first 256 (255?) characters or 
			     *  so getting dropped.
			     */

			    UX_SETUP_PARENT_COMMUNICATION(*pty);
#endif
			    return(NO_ERROR);
			} else {
			    /* if the device is busy, then we should keep
			     * going until we hit one that doesn't exist...
			     */
			    if (errno != EBUSY)
				first = False;
			}
			(void) char_3++;
		    }
		} else {
		    (void) sprintf(ttydev, "%s/tty%c%c",
			    pty_dirs_ptr->pty_dir,
			    *char_1, *char_2);
		    (void) sprintf(ptydev, "%s/pty%c%c",
			    pty_dirs_ptr->ptym_dir,
			    *char_1, *char_2);
		    if ((*pty = open(ptydev, O_RDWR)) >= 0) {
			/* we need to set things up for our next entry into
			 * this function!
			 */
			(void) char_2++;
			/* success... */
#ifdef	HP
			    /*  We need to set the modes here.  If we wait until
			     *  after the program is writing to the pty slave 
			     *  before we do this we end up with some of the
			     *  output getting flushed on us.  It tends to be 
			     *  visible as the first 256 (255?) characters or 
			     *  so getting dropped.
			     */

			    UX_SETUP_PARENT_COMMUNICATION(*pty);
#endif

			return(NO_ERROR);
		    } else {
			/* if the device is busy, then we should keep going
			 * until we hit one that doesn't exist...
			 */
			if (errno != EBUSY)
			    first = False;

		    }
		}
		char_3 = pty_dirs_ptr->char_3;
		(void) char_2++;
	    }
	    char_2 = pty_dirs_ptr->char_2;
	    (void) char_1++;
	}
	first = True;
	(void) pty_dirs_ptr++;
	char_1 = pty_dirs_ptr->char_1;
	char_2 = pty_dirs_ptr->char_2;
	char_3 = pty_dirs_ptr->char_3;
    }

    /* we were unable to allocate a pty master... */
    error_msg(CGETS( MS_MISC_PTY_DRV_ERR, DS_MS_MISC_PTY_DRV_ERR));
    return(ERROR);

#endif /* defined(sgi) */

}


/*----------------------------------------------------------------------
 * NAME:	<static void pass_syserr(msg1, msg2)>
 * DESCRIPTION:	<This function is called by the child process to pass back
                to the parent an error message. >
 * PARAMETERS:	char *msg1, *msg2 -- message to print 
 * RETURN:	void	
 * EXT REFS:	cp_pipe	
 * EXT EFFECTS:	globals or other things altered
 * ASSUMPTIONS:
 * REVISIONS:	22/02/90		creation	
		05/01/93        fix3810	clean-up
		dd/mm/yy	fix#	fix_description
 *--------------------------------------------------------------------*/
static void pass_syserr(msg1, msg2)
char *msg1;
char *msg2;
{
    handshake_t handshake;

    /* let our master know that the open failed... */
    handshake.status = SUBPROC_ERROR;
    handshake.error = errno;
    if (msg1) {
	(void) strcpy(handshake.buffer, msg1);
	if (msg2) {
	    (void) strcat(handshake.buffer, ": ");
	    (void) strcat(handshake.buffer, msg2);
	}
    } else {
	*handshake.buffer = 0;
    }


    (void) write(cp_pipe[1], (char *) &handshake, sizeof(handshake));
    return;
}



/*----------------------------------------------------------------------
 * NAME:	<int UxSetTty()>
 * DESCRIPTION:	<Open the pty slave>
 * PARAMETERS: 	none
 * RETURN:	int - the opened tty	
 * EXT REFS:	cp_pipe, pc_pipe, ttydev	
 * EXT EFFECTS:	ttydev	
 * ASSUMPTIONS:
 * REVISIONS:	05/01/93        fix3810	clean-up	
		dd/mm/yy	fix#	fix_description
 *--------------------------------------------------------------------*/
int UxSetTty()
{
	int tty;
	int i;
	handshake_t handshake;
	static int first = 1;
	static int force_failures = 0;
	int failures = 5;

	if (first) {
	    if (getenv("SUBPROC_PTYOPEN_FAIL"))
		force_failures = 1;
	    first = 0;
	}


	    /* now it is safe to go and open up the pty slave... */
	    while (1) {
		/* The following code allows debugging of slave side
		 * problems openning up the pty slave.
		 */
		if (force_failures && (failures-- > 0)) {


		    tty = -1;
		    errno = EACCES;
		} else {
		    tty = open(ttydev, O_RDWR, 0);
		}

		if (tty >= 0) {

#ifdef	USE_SYSV_PGRP
		    /* We need to make sure that we are actually the
		     * process group leader for the pty.  If
		     * we are, then we should now be able to open
		     * /dev/tty...
		     */
		    if ((i = open("/dev/tty", O_RDWR, 0)) >= 0) {
			/* success!... */
			(void) close(i);
			break;
		    }
#else	/* USE_SYSV_PGRP */
		    break;
#endif	/* USE_SYSV_PGRP */
		}

		/* We were unable to open up a pty.  Inform our master, and
		 * wait for a new one...
		 */
		handshake.status = PTY_BAD;
		handshake.error = errno;
		(void) strcpy(handshake.buffer, ttydev);


		(void) write(cp_pipe[1], (char *) &handshake,
			sizeof(handshake));
		
		/* get reply from parent... */

		if (read(pc_pipe[0], (char *) &handshake, sizeof(handshake)) <
			sizeof(handshake)) {
		    /* parent terminated... */
		    (void) _exit(1);
		}

		if (handshake.status == PTY_NOMORE) {
		    /* No more ptys, let's shutdown... */
		    (void) _exit(1);
		}

		/* We have a new pty to try... */
		(void) UxFree(ttydev);
		ttydev = UxMalloc((unsigned) (strlen(handshake.buffer) + 1));
		if (!ttydev) {
		    (void) _exit(1);
		}

		(void) strcpy(ttydev, handshake.buffer);
	    }
	return(tty);
}


/*----------------------------------------------------------------------
 * NAME:	<void UxPtyChildCommSetup(tty, echo)>
 * DESCRIPTION:	<Set communication parameters for tty slave>
 * PARAMETERS:  int tty - tty for which to set communication parameters
		int echo - do we want echo or not ?
 * RETURN:	void	
 * EXT REFS:	globals_used
 * EXT EFFECTS:	globals or other things altered
 * ASSUMPTIONS:
 * REVISIONS:	05/01/93        fix3810	clean-up
		08/01/93	fix3818	to remove r's at end for HP/IBM
		07/07/93	fix4339	dg88 port
		dd/mm/yy	fix#	fix_description
 *--------------------------------------------------------------------*/
void UxPtyChildCommSetup(tty, echo)
int tty;
int echo;
{
int i;

#ifdef USE_SYSV_TERMIO
#if defined(sgi) || defined(ibmr2)
	    /* If the control tty had its modes screwed around with,
	       eg. by lineedit in the shell, or emacs, etc. then tio
	       will have bad values.  Let's just get termio from the
	       new tty and tailor it.  */
	    if (ioctl (tty, TCGETA, &tio) == -1)
	        pass_syserr(ttydev, "TCGETA");
	    tio.c_lflag |= ECHOE;
#endif /* sgi  or ibmr2 */

	    /* Now is also the time to change the modes of the
	     * child pty.
	     */
	    /* input: nl->nl, don't ignore cr, cr->nl */
	    tio.c_iflag &= ~(INLCR|IGNCR);
	    tio.c_iflag |= ICRNL;
	    /* ouput: cr->cr, nl is not return, no delays, ln->cr/nl */
	    tio.c_oflag &=
		    ~(OCRNL|ONLRET|NLDLY|CRDLY|TABDLY|BSDLY|VTDLY|FFDLY);

/* to remove r's at end */
#if defined(sgi) || defined(Magnum) || defined(HP)  || defined(ibmr2)  || defined(dg88)
            tio.c_oflag &= ~ONLCR;
#else
	    tio.c_oflag |= ONLCR;
#endif /* sgi || magnum || HP || ibmr2  || dg88 */

#ifdef BAUD_0
	    /* baud rate is 0 (don't care) */
	    tio.c_cflag &= ~(CBAUD);
#else	/* !BAUD_0 */
	    /* baud rate is 9600 (nice default) */
	    tio.c_cflag &= ~(CBAUD);
	    tio.c_cflag |= B9600;
#endif	/* !BAUD_0 */

	    /* enable signals, canonical processing (erase, kill, etc),
	    ** echo
	    */
	    tio.c_lflag |= ISIG|ICANON|ECHO;
	    if (!echo)
		tio.c_lflag &= ~ECHO;
	    /* reset EOL to defalult value */
	    tio.c_cc[VEOL] = '@' & 0x3f;		/* '^@'	*/
	    /* certain shells (ksh & csh) change EOF as well */
	    tio.c_cc[VEOF] = 'D' & 0x3f;		/* '^D'	*/
	    if (ioctl (tty, TCSETA, &tio) == -1)
		; /* should probably print an error message... */
#ifdef TIOCSLTC
	    if (ioctl (tty, TIOCSLTC, &ltc) == -1) {
		/* print an error message... */
	        pass_syserr(ttydev, "TIOCSLTC");
	    }
#endif	/* TIOCSLTC */

#ifdef TIOCLSET
	    if (ioctl (tty, TIOCLSET, (char *)&lmode) == -1) {
		/* print an error message... */
	        pass_syserr(ttydev, "TIOCLSET");
	    }
#endif	/* TIOCLSET */

#else	/* USE_SYSV_TERMIO */
	    sg.sg_flags &= ~(ALLDELAY | XTABS | CBREAK | RAW);
	    sg.sg_flags |= ECHO | CRMOD; 
	    sg.sg_flags &= ~CRMOD; /* to remove r's at end */

	    if (!echo)
		sg.sg_flags &= ~ECHO;
	    /* make sure speed is set on pty so that editors work right*/
	    sg.sg_ispeed = B9600;
	    sg.sg_ospeed = B9600;
	    /* reset t_brkc to default value */
	    tc.t_brkc = -1;

	    if (ioctl (tty, TIOCSETP, (char *)&sg) == -1) {
		/* print an error message... */
	        pass_syserr(ttydev, "TIOCSETP");
	    }
	    if (ioctl (tty, TIOCSETC, (char *)&tc) == -1) {
		/* print an error message... */
	        pass_syserr(ttydev, "TIOCSETC");
	    }
	    if (ioctl (tty, TIOCSETD, (char *)&discipline) == -1) {
		/* print an error message... */
	        pass_syserr(ttydev, "TIOCSETD");
	    }
	    if (ioctl (tty, TIOCSLTC, (char *)&ltc) == -1) {
		/* print an error message... */
	        pass_syserr(ttydev, "TIOCSLTC");
	    }
	    if (ioctl (tty, TIOCLSET, (char *)&lmode) == -1) {
		/* print an error message... */
	        pass_syserr(ttydev, "TIOCLSET");
	    }
#endif	/* !USE_SYSV_TERMIO */

	    /* set up stdin, out, err to pty slave... */
	    for (i = 0; i < 3; i++) {
		if (i != tty) {
		    (void) close(i);
		    (void) dup(tty);
		}
	    }

    (void) setpgrp();

}


/*----------------------------------------------------------------------
 * NAME:	<int UxPtyStartChild(pty, echo, args)>
 * DESCRIPTION:	<fork the process and exec a new process with the args
                passed.>
 * PARAMETERS:	int pty -- master pty (for child to close)
                int echo -- echo on or off
                char **args -- the argument list
 * RETURN:	ERROR or new process id	
 * EXT REFS:	pc_pipe, cp_pipe	
 * EXT EFFECTS:	globals or other things altered
 * ASSUMPTIONS:
 * REVISIONS:	05/01/93        fix3810	clean-up
		dd/mm/yy	fix#	fix_description
 *--------------------------------------------------------------------*/
int UxPtyStartChild(pty, echo, args)
int pty;
int echo;
char **args;

{
int pid, i;
int tty;
handshake_t handshake;


	for (i = 0; (i < 10) && ((pid = fork()) < 0); i++) {
	    /* out of resources?... */
	    if (errno != EAGAIN)
		/* no!... */
		break;

	    /* give things a chance to clear up... */
	    (void) sleep((unsigned long) 2);
	}
	if (pid < 0) {
	    UxSyserr(args[0], NULL);
	    return(ERROR);
	}

	if (pid == 0) {

	    /* close the parent's sides of the pipes... */
	    (void) close(cp_pipe[0]);
	    (void) close(pc_pipe[1]);

	    /* make sure the pipes are outside the range of 0, 1, 2... */
	    if (cp_pipe[1] < 3) {
		i = fcntl(cp_pipe[1], F_DUPFD, 3);
		if (i >= 0) {
		    (void) close(cp_pipe[1]);
		    cp_pipe[1] = i;
		}
	    }

	    if (pc_pipe[0] < 3) {
		i = fcntl(pc_pipe[0], F_DUPFD, 3);
		if (i >= 0) {
		    (void) close(pc_pipe[0]);
		    pc_pipe[0] = i;
		}
	    }

#ifdef	USE_SYSV_PGRP
	    /* now, let's kick ourself into a new process group... */
	    (void) setpgrp();
#endif	/* USE_SYSV_PGRP */

	
	tty = UX_SET_CHILD_SIDE();

	    /* Good open...
	     * Set up the pty slave... 
	     */

	(void) UX_SETUP_CHILD_COMMUNICATION(tty, echo);

	    /* mark all other files as close on exec... */
	    UxMarkFileCloseOnExec (1);

	    (void) execvp(args[0], args);

	    /* reset close-on-exec flag */
	    UxMarkFileCloseOnExec (0);

	    pass_syserr(args[0], NULL);
	    /* Added a new handshake type to let the master know that we 
	     * were unable to exec the program and we should return an error.  
	     * Previously, this information was lost and we returned NO_ERROR.
	     */
	    handshake.status = SUBPROC_EXEC_ERROR;
	    (void) strcpy(handshake.buffer, args[0]);

	    (void) write(cp_pipe[1], (char *) &handshake, sizeof(handshake));
	    _exit(1);
	}

      return(pid);
}


/*----------------------------------------------------------------------
 * NAME:	<int set_pseudo(pty)>
 * DESCRIPTION:	<Set the characteristic for the pseudo terminal pair.
   Note:        This routine is the most likely to have portability
                problems.>
 * PARAMETERS: 	int pty -- the pseudo terminal line
 * RETURN:	ERROR/NO_ERROR	
 * EXT REFS:	globals_used
 * EXT EFFECTS:	globals or other things altered
 * ASSUMPTIONS:
 * REVISIONS:	05/01/93        fix3810	clean-up	
		07/07/93	fix4339	dg88 port
		dd/mm/yy	fix#	fix_description
 *--------------------------------------------------------------------*/



#if defined(HP) || defined(_TEK88) || defined(_MOTOR88) || defined(sco) 

int UxSetPseudo(pty)
	int	pty;
{
static int off_flag = 0;
static int *off;
int one= 1;

off = &off_flag;


	/* set master side */
#if !defined(sco) 

#ifndef	Cygwin
	if(ioctl(pty, TIOCREMOTE, off) == -1){
		UxSyserr(ptydev, "TIOCREMOTE");
		return(ERROR);
	}
#endif

	if(ioctl(pty, TIOCPKT, off) == -1){
		UxSyserr(ptydev, "TIOCPKT");
		return(ERROR);
	}

#endif /* sco */


#ifdef HP
	ioctl(pty, TIOCTTY, &one);
#endif
	return(NO_ERROR);
}



#else  /* not HP, _MOTOR88, _TEK88 */

int UxSetPseudo(pty)
int		pty;

{
static int on_flag = 1;
static int off_flag = 0;
static int *on;
static int *off;

on = &on_flag; 
off = &off_flag;


	/* set master side */
#if !defined(sgi) && !defined(ibmr2) && !defined(dg88) && !defined(Cygwin)
	if(ioctl(pty, TIOCREMOTE, off) == -1){
		UxSyserr(ptydev, "TIOCREMOTE");
		return(ERROR);
	}
#endif

#if !defined(DGUX) && !defined(SOLARIS)
	/* Don't use packet mode */
	if(ioctl(pty, TIOCPKT, off) == -1){
		UxSyserr(ptydev, "TIOCPKT");
		return(ERROR);
	}
#endif /* DGUX */

	/* Use non-blocking IO */
	if(ioctl(pty, FIONBIO, on) == -1){
		UxSyserr(ptydev, "FIONBIO");
		return(ERROR);
	}

	/* Don't use asynchronous IO */
#if !defined(sgi) && !defined(magnum) && !defined(ibmr2) && !defined(Cygwin)
	if(ioctl(pty, FIOASYNC, off) == -1){
		UxSyserr(ptydev, "FIOASYNC");
		return(ERROR);
	}
#endif	

	return(NO_ERROR);
}
#endif /* HP || _TEK88 || _MOTOR88 */


/*----------------------------------------------------------------------
 * NAME:	<char ** UxPtyPrepareCommand(command)>
 * DESCRIPTION:	<Prepare command line to be sent to subprocess>
 * PARAMETERS: 	char *command - command line
 * RETURN:	char * - pointer to clean command to be sent to subprocess	
 * 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 ** UxPtyPrepareCommand(command)
char *command;

{
char ** clean_command;
char *proc;
char *proc_args;
int finished;

proc = command;
	for (proc_args = proc, finished = 0; !finished; ) {
		switch (*proc_args) {
			case ' ': case '\t':
				*proc_args++ = '\0';
				/*FALLTHROUGH*/
			case '\0':	/* End of string. */
				finished = 1;
				break;
			default:
				proc_args++;
				break;
		}
	}
	clean_command = UxBuildArgs(proc, proc_args);
return(clean_command);
}


/*----------------------------------------------------------------------
 * NAME:	<int UxPtyExecSubprocess(cmd, echo, t_pty, t_pid)>
 * DESCRIPTION:	<Get the pseudo terminal pair, sets the characteristics
                of it, start the childprocess and sets the output
		side (pty) side as an Input Source to the Xtoolkit.>
 * PARAMETERS: 	char *cmd -- the full command line
 * RETURN:	ERROR/NO_ERROR 	
 * EXT REFS:	cp_pipe, pc_pipe 	
 * EXT EFFECTS:	globals or other things altered
 * ASSUMPTIONS: 05/01/93        fix3810	clean-up
		dd/mm/yy	fix#	fix_description
 *--------------------------------------------------------------------*/
int UxPtyExecSubprocess(cmd, echo, t_pty, t_pid) 
char *cmd;
int echo;
int *t_pty;
int *t_pid;

{
char  **args;

handshake_t handshake;
int 	flags;
int 	pid;
int 	pty;
int	done;


	if(! (args = UX_PREPARE_COMMAND(cmd)) ) {
		return(ERROR);
	}

	if (pipe(pc_pipe) || pipe(cp_pipe)) {
	    UxInternalError(__FILE__, __LINE__,
	    	CGETS( MS_MISC_PIPE_ERR, DS_MS_MISC_PIPE_ERR));
	    return(ERROR);
	}

  	if(UX_SET_PARENT_SIDE(&pty, 1) == ERROR)
    		return(ERROR);

  	if( (pid = UX_START_CHILD(pty, echo, args)) == ERROR) {
		if (pty >= 0)
		    close(pty);
		pty = -1;
    		return(ERROR);
  	}

	/* This is the parent side of the fork...
	 */
	/* close the child's side of the pipes... */
	(void) close(cp_pipe[1]);
	(void) close(pc_pipe[0]);

	for (done = 0; !done; ) {

	    if (read(cp_pipe[0], &handshake, sizeof(handshake)) <= 0) {
		/* Our child is done talking to us.  If it terminated due
		 * to an error, we will catch the death of child and
		 * clean up.
		 */
		(void) close(cp_pipe[0]);
		(void) close(pc_pipe[1]);

		/* we can stop looping now... */
		break;
	    }

	    switch(handshake.status) {
	    case SUBPROC_ERROR:

		/* Child has an error message that they would like us to
		 * print...
		 */
		/* set errno... */
		errno = handshake.error;
		if (*handshake.buffer) {
		    /* they passed us a string to print... */
		    UxSyserr(handshake.buffer, NULL);
		} else {
		    /* no message to print... */
		    UxSyserr(NULL, NULL);
		}
		break;

	    case SUBPROC_EXEC_ERROR:

		/* Child was unable to exec the subprocess...
		 */
		(void) close(cp_pipe[0]);
		(void) close(pc_pipe[1]);
	        /* Just to be safe, don't close the pty unless it is a 
	         * valid file descriptor, and after doing it, make it 
	         * invalid so we won't accidently close it again
	         * (who knows if it will point to anything important).
		 */
		if (pty >= 0)
		    (void) close(pty);
		pty = -1;
		return(ERROR);

	    case PTY_GOOD:

		/* Success!  Let's free up resources and continue...
		 */
		done = True;
		(void) close(cp_pipe[0]);
		(void) close(pc_pipe[1]);
		break;

	    case PTY_BAD:

		/* Pty slave open failed, get them another one...
		 */
		/* get errno from our child... */
		errno = handshake.error;

		if (pty >= 0)
		    (void) close(pty);
		pty = -1;
		if (UX_SET_PARENT_SIDE(&pty, 0) == ERROR) {
		    /* no more ptys... */
		    handshake.status = PTY_NOMORE;

		    (void) write(pc_pipe[1], &handshake, sizeof(handshake));
		    (void) close(cp_pipe[0]);
		    (void) close(pc_pipe[1]);
		    return(ERROR);
		}

		handshake.status = PTY_NEW;
		(void) strcpy(handshake.buffer, ttydev);

		(void) write(pc_pipe[1], &handshake, sizeof(handshake));
		break;


	    default:		/* for PTY_NEW and PTY_NOMORE */
		break;
	    }
	}


  	if( (flags = fcntl(pty, F_GETFL, 0)) == -1) {
		/* CG. 2509002 I believe it meant kill()*/
    		/* signal(SIGKILL, pid);  */
    		kill(pid, SIGKILL);
    		UxSyserr(ptydev, "F_GETFL"); 
		if (pty >= 0)
		    close(pty);
		pty = -1;
    		return(ERROR);
  	}

 	/* O_NDELAY and FNDELAY have the same value. But only by chance ! */
  	if( fcntl(pty, F_SETFL, flags|O_NDELAY) == -1)  {
		/* CG. 2509002 I believe it meant kill()*/
    		/* signal(SIGKILL, pid);  */
    		kill(pid, SIGKILL);
    		UxSyserr(ptydev, "F_SETFL"); 
		if (pty >= 0)
		    close(pty);
		pty = -1;
    		return(ERROR);
  	}

#if !(defined(HP) || defined(sgi) || defined(ibmr2))
	/* set up pty master... */
  	if(UX_SETUP_PARENT_COMMUNICATION(pty) == ERROR) {
		if (pty >= 0)
		    close(pty);
		pty = -1;
    		return(ERROR);
  	}
#endif	/* HP || sgi || ibmr2 */

*t_pid = pid;
*t_pty = pty;

return(NO_ERROR);

}

