wsh/
wsh/binsrc/
wsh/docs/help/
wsh/docs/old/
wsh/etc/
wsh/src/util/
/* Miscellaneous system dependent routines for splitsh */

#include	<sys/types.h>
#include	<sys/stat.h>
#include	<fcntl.h>
#include	<stdio.h>
#include	<signal.h>

#ifdef HAVE_TERMIO_H
#include	<termio.h>
#else
#include	<sys/ioctl.h>
#endif  /* HAVE_TERMIO_H */

#ifdef HAVE_BSDTTY_H
#include	<sys/bsdtty.h>
#ifndef TIOCNOTTY
#define TIOCNOTTY	_IO('t', 113)	/* HP-UX void tty definition */
#endif
#endif /* HAVE_BSDTTY_H */

#ifdef NEED_INET_H
/*#define STTY_HACK*/
#endif

/*
 * Initialize a pty, fork a command running under it, and then 
 * return the master file descriptor
 */

extern int WU_lines, WL_lines, W_columns;	/* From vt100.c */

#define UPPER	0		/* Upper window definition */
#define LOWER	1		/* Lower window definition */

int pty_open(argv, childpid, win)
char *argv[];
int *childpid;
int win;		/* 0 for upper, 1 for lower */
{

	void dropctty(), pty_setwin();
	int get_master_pty(), get_slave_pty();

	char LINES[12], COLUMNS[12];
	int returnfd, slave_fd;
	time_t now;

	/* Get the master pty file descriptor */
	if ( (returnfd=get_master_pty()) < 0 )
		return(-1);

	/* Fork and go! */
	if ( ((*childpid)=fork()) < 0 )
		return(-1);
	else if ( (*childpid) == 0 )
	{
		dropctty();	/* Lose controlling tty */

		if ( (slave_fd=get_slave_pty()) < 0 )
		{
			perror("Can't open slave tty");
			exit(128);
		}
		close(0); close(1); close(2);
		dup(slave_fd); dup(slave_fd); dup(slave_fd);
		close(slave_fd); close(returnfd);

#ifdef TIOCGWINSZ	/* We don't want to set the environment if possible */
		pty_setwin(0, win);
#else
		if ( win == UPPER )
			sprintf(LINES, "LINES=%d", WU_lines);
		else
			sprintf(LINES, "LINES=%d", WL_lines);
		putenv(LINES);
		sprintf(COLUMNS, "COLUMNS=%d", W_columns);
		putenv(COLUMNS);
#endif /* TIOCGWINSZ */
		putenv("TERM=vt100");	/* Put the new TERM in the env. */

#ifdef SIGTSTP
		signal(SIGTSTP, SIG_IGN);
#endif
#ifdef STTY_HACK
		system("stty sane echo echoe intr '^C' erase '^H'");
#else
		(void) tty_reset(0);
#endif
		/* Set our uid to our real uid if necessary */
		(void) setuid(getuid());
			
		/* Run the requested program */
		execvp(argv[0], argv);
		perror("");
		exit(255);
	}
	return(returnfd);
}



/*
 * Pseudo-terminal routines for Unix System V Release 3.2 and BSD4.2-3
 */


extern int errno;
int master_fd;

char tty_name[64];
char pty_name[64];


#ifdef IRIX	/* IRIX System V for SGI machines */

extern char *_getpty();

int get_master_pty()
{

	char 	*ttyptr;

	if ( (ttyptr=_getpty(&master_fd, O_RDWR, 0600, 0)) == 0 )
		return(-1);
	else
		strcpy(tty_name, ttyptr);

	return(master_fd);
}

/*
 * Open the slave half of a pseudo-terminal.
 */

int get_slave_pty()
{
	int	slave_fd;
	char	*slavename;

	slavename=tty_name;

	if (slavename == NULL) {
		close(master_fd);
		return(-1);
	}

	if ( (slave_fd=open(slavename, O_RDWR)) < 0 )	/* open the slave */
	{
		close(master_fd);
		return(-1);
	}

	return(slave_fd);
}
#else /* ! IRIX */


#ifdef SOLARIS		/* System V.4 pty routines from W. Richard Stevens */

#include <stropts.h>

#define DEV_CLONE	"/dev/ptmx"

extern char *ptsname();

int get_master_pty()
{

	char 	*ttyptr;

	if ( (master_fd=open(DEV_CLONE, O_RDWR)) < 0 )
		return(-1);

	if ( grantpt(master_fd) < 0 )	/* grant access to slave */
	{
		close(master_fd);
#ifdef DEBUG
		perror("grantpt()");
#endif
		return(-1);
	}

	if ( unlockpt(master_fd) < 0 )	/* clear slave's lock flag */
	{
		close(master_fd);
		return(-1);
	}

	if ( (ttyptr=ptsname(master_fd)) == NULL )
	{
		close(master_fd);
		return(-1);
	}
	else
		strcpy(tty_name, ttyptr);

	return(master_fd);
}

/*
 * Open the slave half of a pseudo-terminal.
 */

int get_slave_pty()
{
	int	slave_fd;
	char	*slavename;

	slavename=tty_name;

	if ( (slave_fd=open(slavename, O_RDWR)) < 0 )	/* open the slave */
	{
		close(master_fd);
		return(-1);
	}

	if ( ioctl(slave_fd, I_PUSH, "ptem") < 0 )
	{
		close(master_fd);
		close(slave_fd);
		return(-1);
	}

	if ( ioctl(slave_fd, I_PUSH, "ldterm") < 0 )
	{
		close(master_fd);
		close(slave_fd);
		return(-1);
	}

	if ( ioctl(slave_fd, I_PUSH, "ttcompat") < 0 )
	{
		close(master_fd);
		close(slave_fd);
		return(-1);
	}

	return(slave_fd);
}

#else	/* BSD, Sun/OS, AIX, ULTRIX, HP-UX, AT&T SYSV */

#include	<setjmp.h>

#ifndef X_OK
#define	R_OK	4	/* Test for Read permission */
#define	W_OK	2	/* Test for Write permission */
#define	X_OK	1	/* Test for eXecute permission */
#endif  /* X_OK */

#define PTY_OWNER 	0	/* the uid of the owner of pty's.
				   usually bin or root */
jmp_buf next;

void trynext()
{
	longjmp(next, 2);
}


int get_master_pty()
{
	int i, master_fd;
	char *ptr;
	struct stat statbuff;
	static char ptychar[]="pqrs";  /* X */ /* 'r' is also a valid letter. */
	static char hexdigit[]="0123456789abcdef";  /* Y */

	for (ptr=ptychar; *ptr != 0; ptr++)
	{
		strcpy(pty_name, "/dev/ptyXY");
		pty_name[8]=(*ptr);  /* X */
		pty_name[9]='0';   /* Y */

		if ( access(pty_name, R_OK|W_OK) != 0 )
			break;

#ifdef OLDDEBUG
		fprintf(stderr, "statted.\n");
#endif
		for ( i=(-1); ; )
		{
			/* Set a time limit for the open */
			if ( setjmp(next) == -1 )
				return(-1);
			else
			{
				if ( ++i >= 16 )
					break;
#ifdef OLDDEBUG
				perror(pty_name);
#endif
			}

			signal(SIGALRM, trynext);
			alarm(2);

			pty_name[5]='p';
			pty_name[9]=hexdigit[i];

			if ( (master_fd=open(pty_name, O_RDWR)) >= 0 )
			{
				pty_name[5]='t';
				sprintf(tty_name, "%s", pty_name);

#ifdef OLDDEBUG
				fprintf(stderr, "tty: %s\n", tty_name);
#endif
				if ( access(tty_name, R_OK|W_OK) == 0 &&
				     stat(tty_name, &statbuff) >= 0 )
				{
					if ( statbuff.st_uid == PTY_OWNER )
					{
#ifdef OLDDEBUG
						fprintf(stderr," Slave pty: %s\n", tty_name);
#endif 
						/* Reset the alarm */
						alarm(0);

						return (master_fd);
					}
				}
				else
				{
					pty_name[5]='p';
					(void) close(master_fd);
				}
			}
			/* reset the alarm */
			alarm(0);
		}
	}
	return(-1);
}

/* Open the slave half of a pseudo-terminal. */

int get_slave_pty()
{
	int slave_fd;

	errno=0;

	/* Set a time limit for the open */
	alarm(3);

	if ( (slave_fd=open(tty_name, O_RDWR)) < 0 )
	{
		close(master_fd);
		return(-1);
	}

	/* reset the alarm */
	alarm(0);
	return(slave_fd);
}

#endif  /* if SOLARIS */
#endif  /* if IRIX */


/* These are the binary data functions that I am using instead of 
   bcopy() and bzero(), written by Richard A. O'Keefe.
     Thanks!
*/

void d_copy(src, dst, len)
    register char *src, *dst;
    register int len;
    {
	while (--len >= 0) *dst++ = *src++;
    }

void d_zero(dst, len)
    register char *dst;
    register int len;
    {
	while (--len >= 0) *dst++ = 0;
    }



/* Here are the Terminal manipulation routines...  */


/* Code to disassociate from my tty. Yay! :) */

void dropctty()
{
	int fd;

#ifdef TIOCNOTTY  /* We want to get HP-UX, BSD, and Sun/OS here */
	setpgrp(0, 0);

#ifndef CIBAUD   /* Sun/OS doesn't need to do TIOCNOTTY.  */
	if ( (fd=open("/dev/tty", O_RDWR)) > (-1) ) 
	{
		if (ioctl(fd, TIOCNOTTY, 0) < 0)
		{
			perror("ioctl TIOCNOTTY error");
			fprintf(stderr, "\r");
		}
		close(fd);
	}
#endif /* CIBAUD */
#else /*   SYSV 	*/
	setpgrp();
#endif /* TIOCNOTTY */
}


#ifdef HAVE_TERMIO_H

/* Get the modes of the controlling tty and save them.  Saves
   ttymodes in tty_mode and returns -1 if ioctl fails. */

struct termio tty_mode;  /* Save tty mode here */
static int tty_init=0;

int tty_getmode(fd)
int fd;
{
	d_zero((char *)&tty_mode, sizeof(struct termio));
	tty_init=1;	/* Flag: we have initialized the tty_mode struct */

	if ( ! isatty(fd) )
		return(0);

#ifdef OLDDEBUG
	fprintf(stderr, "Getting tty modes for tty_mode.\r\n");
#endif

	if (ioctl(fd, TCGETA, (char *) &tty_mode) < 0)
	{
#ifdef DEBUG
		perror("tty_getmode(): ioctl error");
#endif
		return(-1);  
	}

	return(0);
}


/* Set a tty to a sane mode */

int tty_sane(fd)
int fd;
{
	struct termio temp_mode;

	if ( ! isatty(fd) )
		return(0);

#ifdef OLDDEBUG
	fprintf(stderr, "tty_init: %d\nfd: %d\n", tty_init, fd);
#endif

	if ( ! tty_init )
	{
		if (ioctl(fd, TCGETA, (char *) &tty_mode) < 0)
			return(-1);  
	}
/*
	temp_mode.c_lflag=(tty_mode.c_lflag|(ISIG|ICANON|ECHO|ECHOE|ECHOK));
	temp_mode.c_iflag=(tty_mode.c_iflag|(BRKINT|IGNPAR|ISTRIP|ICRNL|IXON));
	temp_mode.c_oflag=(tty_mode.c_oflag|(OPOST|ONLCR));
	temp_mode.c_cflag=(tty_mode.c_cflag|(CS7|PARENB|CREAD));
	temp_mode.c_cc[VERASE]=('H'^64);
	temp_mode.c_cc[VKILL]=('U'^64);
	temp_mode.c_cc[VQUIT]=('\\'^64);
	temp_mode.c_cc[VINTR]=('C'^64);
	temp_mode.c_cc[VEOF]=('D'^64);
*/	
	if ( ioctl(fd, TCSETA, (char *)&temp_mode) < 0 )
	{
#ifdef DEBUG
		perror("Can't set tty modes");
#endif
		return(-1);
	}

	return(0);
}


/* Set a terminal in raw mode */

int tty_raw(fd)
int fd;     /* of tty device */
{
	struct termio temp_mode;

	if ( ! tty_init )
		return(-1);

	if ( ! isatty(fd) )
		return(0);

	if ( ioctl(fd, TCGETA, (char *)&temp_mode) < 0 )
		return(-1);

	temp_mode.c_iflag=(IGNBRK | ISTRIP);   /* turn off all input control */
	temp_mode.c_oflag &= ~(OLCUC | ONLCR | OCRNL | ONLRET);
					/* disable output post-processing */
	temp_mode.c_lflag = 0;
	temp_mode.c_cc[VMIN]=1;		/* 1 or more chars satisfy read */
	temp_mode.c_cc[VTIME]=0;	/* 10'ths of seconds between chars */

	if (ioctl(fd, TCSETA, (char *) &temp_mode) < 0)
		return(-1);
	return(0);
}


/* Restore terminal's mode to whatever it was on the most
   recent call to the tty_getmode() function. */

int tty_reset(fd)
int fd;
{
	if ( ! tty_init )
		return(-1);

	if ( ! isatty(fd) )
		return(0);

	if (ioctl(fd, TCSETA, (char *) &tty_mode) < 0)
		return(-1);
	return(0);
}

#else  /* no /usr/include/termio.h */
#ifdef NEED_COMPAT_H		/* FreeBSD needs this */
#include <sys/ioctl_compat.h>
#endif /* NEED_COMPAT_H */

/* Set a tty to a sane mode */

int tty_sane(fd)
int fd;
{
	struct sgttyb temp_mode;

	if ( ! isatty(fd) )
		return(0);

	if (ioctl(fd, TIOCGETP, (char *) &temp_mode) < 0)
		return(-1);

	temp_mode.sg_flags|=ECHO;
	
	if (ioctl(fd, TIOCSETP, (char *) &temp_mode) < 0)
		return(-1);

	return(0);
}



/* Get the modes of the controlling tty and save them.  Saves
   ttymodes in tty_mode and returns 1 if ioctl fails. */

static struct sgttyb	tty_mode;	/* save tty mode here */

int tty_getmode(fd)
int fd;
{
	if ( ! isatty(fd) )
		return(0);

	if (ioctl(fd, TIOCGETP, (char *) &tty_mode) < 0)
		return(-1);

	return(0);
}

/*
 * Put a terminal device into RAW mode with ECHO off.
 * Before doing so we first save the terminal's current mode,
 * assuming the caller will call the tty_reset() function
 * (also in this file) when it's done with raw mode.
 */

int tty_raw(fd)
int	fd;		/* of terminal device */
{
	struct sgttyb	temp_mode;

	if ( ! isatty(fd) )
		return(0);

	temp_mode = tty_mode;

	temp_mode.sg_flags |= RAW;	/* turn RAW mode on */
	temp_mode.sg_flags &= ~ECHO;	/* turn ECHO off */
	if (ioctl(fd, TIOCSETP, (char *) &temp_mode) < 0)
		return(-1);

	return(0);
}

/*
 * Restore a terminal's mode to whatever it was on the most
 * recent call to the tty_getmode() function above.
 */

int tty_reset(fd)
int	fd;		/* of terminal device */
{
	if ( ! isatty(fd) )
		return(0);

	if (ioctl(fd, TIOCSETP, (char *) &tty_mode) < 0)
		return(-1);

	return(0);
}
#endif /* HAVE_TERMIO_H */


/* Set the pty window size to the size of the virtual window */

#ifdef TIOCSWINSZ

static struct /* winsize */ {
		unsigned short	ws_row;		/* rows, in characters */
		unsigned short	ws_col;		/* columns, in characters */
		unsigned short	ws_xpixel;	/* horizontal size - not used */
		unsigned short	ws_ypixel;	/* vertical size - not used */
	} mywinz;

void pty_setwin(fd, win)
int fd;			/* The pty file descriptor */
int win;		/* 0 for upper, 1 for lower window */
{
	if ( win == UPPER )
		mywinz.ws_row=WU_lines;
	else
		mywinz.ws_row=WL_lines;
	mywinz.ws_col=W_columns;
	mywinz.ws_xpixel=0;
	mywinz.ws_ypixel=0;
	(void) ioctl(fd, TIOCSWINSZ, &mywinz);
}

#else
void pty_setwin(fd, win)
int fd;
int win;
{
	/* Bogus routine */
}
#endif /* TIOCSWINSZ */

/*
 * Write "n" bytes to a descriptor.
 * Use in place of write() when fd is a stream socket.
 */

int writen(fd, ptr, nbytes)
register int	fd;
register char	*ptr;
register int	nbytes;
{
	int	nleft, nwritten;

	nleft = nbytes;
	while (nleft > 0) {
		nwritten = write(fd, ptr, nleft);
		if (nwritten <= 0)
			return(nwritten);		/* error */

		nleft -= nwritten;
		ptr   += nwritten;
	}
	return(nbytes - nleft);
}