/* pty_termios.c - routines to allocate ptys - termios version

Written by: Don Libes, NIST, 2/6/90

Design and implementation of this program was paid for by U.S. tax
dollars.  Therefore it is public domain.  However, the author and NIST
would appreciate credit if this program or parts of it are used.

*/

#include "exp_conf.h"
#ifdef HAVE_UNISTD_H
#  include <unistd.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>

#ifdef HAVE_SYSMACROS_H
#include <sys/sysmacros.h>
#endif

#ifdef HAVE_PTYTRAP
#include <sys/ptyio.h>
#endif

#include <sys/file.h>

#ifdef HAVE_SYS_FCNTL_H
#  include <sys/fcntl.h>
#else
#  include <fcntl.h>
#endif

#ifdef HAVE_PTMX
#  include <sys/stropts.h>
#endif

#include "exp_tty.h"
#include "exp_rename.h"
#include "exp_pty.h"

void debuglog();

extern int errno;
extern char *sys_errlist[];

#ifndef TRUE
#define TRUE 1
#define FALSE 0
#endif

#if defined(HAVE__GETPTY) || defined(HAVE_PTC) || defined(HAVE_PTC_PTS) || defined(HAVE_PTMX)
static char *slave_name;
#endif

#if defined(HAVE_GETPTY)
#include <sys/vty.h>
static char	master_name[MAXPTYNAMELEN];
static char	slave_name[MAXPTYNAMELEN];
#endif

#if !defined(HAVE_GETPTY) && !defined(HAVE_GETPTY) && !defined(HAVE_PTC) && !defined(HAVE_PTC_PTS) && !defined(HAVE_PTMX)
#ifdef HAVE_PTYM
static char	master_name[] = "/dev/ptym/ptyXX";
static char	slave_name[] = "/dev/pty/ttyXX";
static char	*slave_bank;
static char	*slave_num;
#else
static char	master_name[] = "/dev/ptyXX";
static char	slave_name [] = "/dev/ttyXX";
#endif
#endif

static char	*tty_type;		/* ptr to char [pt] denoting
					   whether it is a pty or tty */
static char	*tty_bank;		/* ptr to char [p-z] denoting
					   which bank it is */
static char	*tty_num;		/* ptr to char [0-f] denoting
					   which number it is */
static void
pty_stty(s,name)
char *s;		/* args to stty */
char *name;		/* name of pty */
{
#define MAX_ARGLIST 10240
	char buf[MAX_ARGLIST];	/* overkill is easier */

	sprintf(buf,"stty %s < %s > %s",s,name,name);
	system(buf);
}

int exp_dev_tty;	/* file descriptor to /dev/tty or -1 if none */
int knew_dev_tty;	/* true if we had our hands on /dev/tty at any time */

#define GET_TTYTYPE	0
#define SET_TTYTYPE	1
static void
ttytype(request,fd,ttycopy,ttyinit,s)
int request;
int fd;
		/* following are used only if request == SET_TTYTYPE */
int ttycopy;	/* true/false, copy from /dev/tty */
int ttyinit;	/* if true, initialize to sane state */
char *s;	/* stty args */
{
	if (request == GET_TTYTYPE) {
#ifdef POSIX
		if (-1 == tcgetattr(fd, &exp_tty_original)) {
#else
		if (-1 == ioctl(fd, TCGETS, (char *)&exp_tty_original)) {
#endif
			knew_dev_tty = FALSE;
			exp_dev_tty = -1;
		}
	} else {	/* type == SET_TTYTYPE */
		if (ttycopy && knew_dev_tty) {
#ifdef POSIX
			(void) tcsetattr(fd, TCSADRAIN, &exp_tty_original);
#else
			(void) ioctl(fd, TCSETS, (char *)&exp_tty_original);
#endif
		}

#ifdef __SABER__
#undef DFLT_STTY
#define DFLT_STTY "sane"
#endif
		if (ttyinit) {
			/* overlay parms originally supplied by Makefile */
			debuglog("getptyslave: (default) stty %s\n",DFLT_STTY);
			pty_stty(DFLT_STTY,slave_name);
		}

		/* lastly, give user chance to override any terminal parms */
		if (s) {
			/* give user a chance to override any terminal parms */
			debuglog("getptyslave: (user-requested) stty %s\n",s);
			pty_stty(s,slave_name);
		}
	}
}

void
init_pty()
{
#if !defined(HAVE_GETPTY) && !defined(HAVE_GETPTY) && !defined(HAVE_PTC) && !defined(HAVE_PTC_PTS) && !defined(HAVE_PTMX)
#ifdef HAVE_PTYM
	static char dummy;
	tty_bank = &master_name[strlen("/dev/ptym/pty")];
	tty_num  = &master_name[strlen("/dev/ptym/ptyX")];
	slave_bank = &slave_name[strlen("/dev/pty/tty")];
	slave_num  = &slave_name[strlen("/dev/pty/ttyX")];
#else
	tty_bank = &master_name[strlen("/dev/pty")];
	tty_num  = &master_name[strlen("/dev/ptyp")];
	tty_type =   &slave_name[strlen("/dev/")];
#endif

#endif /* HAVE_PTYM */


	exp_dev_tty = open("/dev/tty",O_RDWR);
	knew_dev_tty = (exp_dev_tty != -1);
	if (knew_dev_tty) ttytype(GET_TTYTYPE,exp_dev_tty,0,0,(char *)0);
}

#ifndef R_OK
/* 3b2 doesn't define these according to jthomas@nmsu.edu. */
#define R_OK 04
#define W_OK 02
#endif

int
getptymaster()
{
	char *hex, *bank;
  	struct stat stat_buf;
	int master = -1;

#if defined(HAVE_PTMX)
#if defined(HAVE_PTMX_BSD)
        if ((master = open("/dev/ptmx_bsd", O_RDWR)) == -1) return(-1);
#else
        if ((master = open("/dev/ptmx", O_RDWR)) == -1) return(-1);
#endif
	if ((slave_name = ptsname(master)) == NULL || unlockpt(master) || grantpt(master)) {
		close(master);
		return(-1);
	}
	(void) ioctl(master,TIOCFLUSH,(char *)0);

	return(master);
#endif

#if defined(HAVE__GETPTY)		/* SGI needs it this way */
	slave_name = _getpty(&master, O_RDWR, 0600, 0);
	if (slave_name == NULL)
     return (-1);	
	return(master);
#endif

#if defined(HAVE_PTC) && !defined(HAVE__GETPTY)	/* old SGI, version 3 */
	master = open("/dev/ptc", O_RDWR);
	if (master >= 0) {
     int ptynum;

		if (fstat(master, &stat_buf) < 0) {
			close(master);
       return(-1);
     }
     ptynum = minor(stat_buf.st_rdev);
		sprintf(slave_name,"/dev/ttyq%d",ptynum);
   }
	return(master);
#endif

#if defined(HAVE_GETPTY) && !defined(HAVE__GETPTY)
	master = getpty(master_name, slave_name, O_RDWR);
	/* is it really necessary to verify slave side is usable? */
	return master;
#endif

#if defined(HAVE_PTC_PTS)
	master = open("/dev/ptc",O_RDWR);
	if (master >= 0) {
		/* never fails */
		slave_name = ttyname(master);
	}
	return(master);
#endif

#if !defined(HAVE_GETPTY) && !defined(HAVE_GETPTY) && !defined(HAVE_PTC) && !defined(HAVE_PTC_PTS) && !defined(HAVE_PTMX)
	if (exp_pty_test_start() == -1) return -1;

	for (bank = "pqrstuvwxyzPQRSTUVWXYZ";*bank;bank++) {
		*tty_bank = *bank;
    *tty_num = '0';
		if (stat(master_name, &stat_buf) < 0) break;
    for (hex = "0123456789abcdef";*hex;hex++) {
      *tty_num = *hex;
#ifdef HAVE_PTYM
	*slave_bank = *tty_bank;
	*slave_num = *tty_num;
#else
			strcpy(slave_name,master_name);
	*tty_type = 't';
#endif
			master = exp_pty_test(master_name,slave_name,*tty_bank,*tty_num);
			if (master >= 0) goto done;
		}
	}
 done:
#ifdef HAVE_PTYTRAP
	/* Turn on trapping of close, open and */
	/* ioctl requests from the slave.*/
	{
		int enable = 1;
	ioctl(master, TIOCTRAP, &enable);
	}
#endif /* HAVE_PTYTRAP */

	exp_pty_test_end();
	return(master);
#endif
}

int
getptyslave(ttycopy,ttyinit,stty_args)
int ttycopy;
int ttyinit;
char *stty_args;
{
	int slave;
	char buf[1024];

	if (0 > (slave = open(slave_name, O_RDWR))) return(-1);

#if defined(HAVE_PTMX)
#if defined(HAVE_PTMX_BSD)
        if (ioctl (slave, I_LOOK, buf) != 0)
                if (ioctl (slave, I_PUSH, "ldterm")) {
                        debuglog("ioctl(%s,I_PUSH,\"ldterm\") = %s\n",sys_errlis
t[errno]);
        }
#else
	if (ioctl(slave, I_PUSH, "ptem")) {
		debuglog("ioctl(%s,I_PUSH,\"ptem\") = %s\n",sys_errlist[errno]);
	}
	if (ioctl(slave, I_PUSH, "ldterm")) {
		debuglog("ioctl(%s,I_PUSH,\"ldterm\") = %s\n",sys_errlist[errno]);
	}
	if (ioctl(slave, I_PUSH, "ttcompat")) {
		debuglog("ioctl(%s,I_PUSH,\"ttcompat\") = %s\n",sys_errlist[errno]);
	}
#endif
#endif

	/* sanity check - if slave not 0, skip rest of this and return */
	/* to what will later be detected as an error in caller */
	if (0 != slave) return(slave);

	fcntl(0,F_DUPFD,1);	/* duplicate 0 onto 1 to prepare for stty */
	ttytype(SET_TTYTYPE,slave,ttycopy,ttyinit,stty_args);
	(void) exp_pty_unlock();
	return(slave);
}

#ifdef HAVE_PTYTRAP
#include <sys/ptyio.h>
#include <sys/time.h>

/* return 1 for failure, 0 for success */
int
exp_wait_for_slave_open(fd)
int fd;
{
	fd_set excep;
	struct timeval t;
	struct request_info ioctl_info;
	int rc;

	int maxfds = sysconf(_SC_OPEN_MAX);

	t.tv_sec = 30;	/* 30 seconds */
	t.tv_usec = 0;

	FD_ZERO(&excep);
	FD_SET(fd,&excep);

	rc = select(maxfds,(fd_set *)0,(fd_set *)0,&excep,&t);
	if (rc != 1) {
		debuglog("spawned process never started, errno = %d\n",errno);
		return(1);
	}
	if (ioctl(fd,TIOCREQCHECK,&ioctl_info) < 0) {
		debuglog("ioctl(TIOCREQCHECK) failed, errno = %d\n",errno);
		return(1);
	}
	if (ioctl_info.request != TIOCOPEN) {
		debuglog("ioctl from slave is not OPEN?\n");
	}
	if (ioctl(fd, TIOCREQSET, &ioctl_info) < 0) {
		debuglog("ioctl(TIOCREQSET) failed, errno = %d\n",errno);
		return(1);
	}
	return(0);	
}
#endif
