wsh/
wsh/binsrc/
wsh/docs/help/
wsh/docs/old/
wsh/etc/
wsh/src/util/
/*  tty.c   Shareware Copyright by Sam Lantinga	5/6/93	*/

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

extern void d_zero(), d_copy();

#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_COMPAT_H
#include	<sys/ioctl_compat.h>
#endif /* NEED_COMPAT_H */

/*
 * Pseudo-terminal routines for Unix System V Release 3.2 and BSD4.2-3
 * I actually have three sets of routines, for three different methods
 * of obtaining a pseudo-tty.  The first is for IRIX System V UNIX, 
 * which uses _getpty() to obtain a pty.  The second is for Solaris
 * 2.x which uses a clone open with some funky streams stuff to get
 * a pseudo-tty.  The last is the brute-force method that works on
 * every thing else I have come across, including Sun/OS 4.x, AIX,
 * HP-UX, BSD 4.2-3, ULTRIX, and AT&T System V.3
 */


extern int errno;
int master_fd;


char tty_name[18];
char pty_name[12];


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

extern char *_getpty();

int get_pty_master()
{

	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_pty_slave()
{
	int	slave_fd;
	char	*slavename;

	slavename=tty_name;

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

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

	return(slave_fd);
}

#else  /* not IRIX */

#ifdef SOLARIS		/* Solaris 2.1 (UNIX System V r4)  */

#include <stropts.h>

#define DEV_CLONE	"/dev/ptmx"

extern char *ptsname();

int get_pty_master()
{

	char 	*ttyptr;

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

	if ( grantpt(master_fd) < 0 )	/* grant access to slave */
	{
		close(master_fd);
		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_pty_slave()
{
	int	slave_fd;
	char	*slavename;

	slavename=tty_name;

	if ( (slave_fd=open(slavename, O_RDWR, 0666)) < 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

#define PTY_OWNER 	0	/* the uid of the owner of pty's.
				   usually bin or root */
#define BIN_UID		2	/* Secondary pty owner */

jmp_buf next;

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


int get_pty_master()
{
	int i, master_fd;
	char *ptr;
	struct stat statbuff;
	static char ptychar[]="pqrst";  /* X */ 
	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 ( stat(pty_name, &statbuff) < 0 )
			break;
#ifdef OLDDEBUG
		fprintf(stderr, "statted.\n");
#endif
		i=(-1);		/* Initialize i */

		/* Set a time limit for the open */
		if ( setjmp(next) == -1 )
			return(-1);
		signal(SIGALRM, trynext);

		for ( ++i; hexdigit[i]; ++i)
		{
			pty_name[5]='p';
			pty_name[9]=hexdigit[i];

			alarm(2);	/* Set an open timeout */

			if ( (master_fd=open(pty_name, O_RDWR, 0666)) >= 0 )
			{
				alarm(0); 	/* Reset the alarm */

				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 ) {
					signal(SIGALRM, SIG_DFL);
					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_pty_slave()
{
	int slave_fd;

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

#endif  /* is SOLARIS? */
#endif /* is IRIX? */ /* The end of the Pseudo-tty routines */



/* These are the terminal manipulation routines. :)  Fun!  */


#ifdef SIGWINCH		 /* Starting window structure */

/* I am using struct winsize here, but redefining it, because
   Each system seems to define it in a different place, or not
   at all.  The actual structure never seems to change though. :)
 */

struct winstats {
	unsigned short	ws_row;		/* rows, in characters */
	unsigned short	ws_col;		/* columns, in characters */
	unsigned short	ws_xpixel;	/* horizontal size, pixels - not used */
	unsigned short	ws_ypixel;	/* vertical size, pixels - not used */
	} tty_win;


#ifdef NEED_UPDATEWIN			/* Not used */

/* The window size has changed, let the pty know.  Used as a signal handler */

void updatewin()
{
#ifdef TIOCGWINSZ
    	(void) ioctl(ttyfd, TIOCGWINSZ, &tty_win);
    	(void) ioctl(masterfd, TIOCSWINSZ, &tty_win);
#endif /* TIOCGWINSZ */

	   /* An interesting note...
		I had code here to send a SIGWINCH to the pty
		process, but it turns out the kernel does when
		the pty recieves the TIOCSWINSZ ioctl.  */
}
#endif /* NEED_UPDATEWIN */

#endif /* SIGWINCH */
	

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

void dropctty()
{
	int fd;

#ifdef _POSIX_SOURCE
	setsid();		/* The POSIX solution is simple. :) */
#else
#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 */
#endif /* _POSIX_SOURCE */
}

/* Just fork, and exit, leaving the child running as a daemon */
void daemonize()
{
	switch (fork()) {
		case -1:	perror("fork() failed");
				exit(3);
				break;
		case 0:		dropctty();
				return;
		default:	exit(0);
	}
}

#ifdef HAVE_TERMIO_H

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

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

int tty_getmode(fd)
int fd;
{

	/* If not a tty, don't set tty_init, but return ok */
	if ( !isatty(fd) ) 
		return(0);

	d_zero((char *)&tty_mode, sizeof(struct termio));

	if (ioctl(fd, TCGETA, (char *) &tty_mode) < 0)
		return(-1);  
	tty_init=1;	/* Flag: we have initialized the tty_mode struct */

#if defined(SIGWINCH) && defined(TIOCGWINSZ)
	d_zero((char *)&tty_win, sizeof(struct winstats));

   	(void) ioctl(fd, TIOCGWINSZ, &tty_win);
#endif /* SIGWINCH */

	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);

#if defined(SIGWINCH) && defined(TIOCSWINSZ)
	(void) ioctl(fd, TIOCSWINSZ, &tty_win);
#endif /* SIGWINCH */

	return(0);
}


/* Set a tty to a sane mode */

int tty_sane(fd)
int fd;
{
#ifdef TIOCGWINSZ
	struct winstats temp_win;
#endif
	struct termio temp_mode;

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

#ifdef DEBUG
	fprintf(stderr, "tty_init: %d\r\n", tty_init);
#endif

	temp_mode.c_lflag=(ISIG|ICANON|ECHO|ECHOE);
	temp_mode.c_iflag=(BRKINT|IGNPAR|ISTRIP|ICRNL|IXON);
	temp_mode.c_oflag=(OPOST|ONLCR);
	temp_mode.c_cflag=(CS8|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);
	temp_mode.c_cc[VMIN]=1;
	temp_mode.c_cc[VTIME]=0;
	
	if (ioctl(fd, TCSETA, (char *) &temp_mode) < 0)
		return(-1);

#if defined(SIGWINCH) && defined(TIOCSWINSZ)
	d_zero((char *)&temp_win, sizeof(struct winstats));
	temp_win.ws_row=24;
	temp_win.ws_col=80;
	(void) ioctl(fd, TIOCSWINSZ, &temp_win);
#endif
	return(0);
}


/* Set a terminal in raw mode */

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

	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);
}


/* Function to set a tty echo or no echo */

int tty_echo(fd, echo)
int fd;
int echo;
{
	struct termio temp_mode;

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

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

	if ( echo )
		temp_mode.c_lflag|=ECHO;
	else
		temp_mode.c_lflag&=(~ECHO);

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

#else  /* no /usr/include/termio.h */


/* 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_init=0;

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

	tty_init=1;	/* Flag: we have initialized the tty_mode struct */

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

#ifdef SIGWINCH
	if ( ioctl(fd, TIOCGWINSZ, &tty_win) < 0 )
		perror("ioctl TIOCGWINSZ error");
#endif /* SIGWINCH */

	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 ( ! tty_init )	/* Have we been initialized? */
		return(-1);

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

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

#ifdef SIGWINCH
    	(void) ioctl(fd, TIOCSWINSZ, &tty_win);
#endif /* SIGWINCH */

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

	return(0);
}

/*
 * Put a terminal device into RAW mode with ECHO off.
 */

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

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

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

	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);
}


/* Set a terminal echo or no echo, as requested.  */

int tty_echo(fd, echo)
int fd;
int echo;
{
	struct sgttyb temp_mode;

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

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

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

	return(0);
}

#endif /* HAVE_TERMIO_H */

/*
 * 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);
}

/* Replacement for bzero(), which isn't always available.  */

void d_zero(data, size)
char *data;
int   size;
{
	int i;

	for ( i=0; (i < size); ++i, ++data )
		*data='\0';
}

/* Replacement for bcopy(), which isn't always available.  */

void d_copy(frombuf, tobuf, size)
char *frombuf;
char *tobuf;
int   size;
{
	int i;

	for ( i=0; (i < size); ++i, ++frombuf, ++tobuf )
		*tobuf=(*frombuf);
}