/* Copyright (c)1994-2000 Begemot Computer Associates. All rights reserved.
 * See the file COPYRIGHT for details of redistribution and use. */

/*
 * dev_xty - xterm tty
 *
 * should perhaps disallow user window size changes
 *
 */
# include <stdio.h>
# include <stdlib.h>
# include <unistd.h>
# include <string.h>
# include <fcntl.h>
# include <errno.h>
# include <sys/types.h>
# include <sys/socket.h>
# include <sys/wait.h>
# include <netinet/in.h>
# include <signal.h>
# include <stdarg.h>
# include <termios.h>
# include <stdarg.h>
# include <begemot.h>

# include "cdefs.h"
# include "util.h"
# include "pathnames.h"
# include "tty.h"

RCSID("$Id: tty_xterm.c 461 2000-12-04 17:03:43Z hbb $")

/*
# define DEBUG		"/dev/ttyp0" 
*/

# ifdef DEBUG
#  define D(C)	fprintf C
# else
#  define D(C)
# endif

void		open_pty(void);
void		fork_xterm(void);
void		get_winId(void);
void		wait_child(void);


int	master;			/* master side of pty */
int	slave;			/* slave side of pty */
char	Sc[3] = "p0";		/* slave name */
pid_t	xpid;			/* xterm's pid */
int	winId;			/* xterms window Id */
char	*geometry;		/* user specified geometry */
char	*title;			/* user specified title */
char	*pos;			/* user specified position */
int	iconic;			/* start iconified */
char	**args;			/* pointer to args to be passed to xterm */
int	cs7;

# ifdef DEBUG
FILE	*dbg;
# endif


static char usgtxt[] =
"Begemot PDP11 emulator Version %s; Terminal-to-xterm adapter\n"
"Copyright (c) 1994-2000 Begemot Computer Associates. All rights reserved.\n"
"Usage: tty_xterm [-7] [-g geo] [-t title] [-p pos] [-i] [-x args...]\n"
"where:\n"
"	-7	mask to 7bits\n"
"	-g geo	specify the X11-geometry string for the xterm\n"
"	-t tit	specify a window title\n"
"	-p pos	specify a window position\n"
"	-i	start iconified\n"
"	-x 	pass the rest of the arguments to the xterm\n";

static void usage(int) DEAD_FUNC;

static void
usage(int e)
{
	fprintf(stderr, usgtxt, VERSION);
	exit(e);
}


int
main(int argc, char *argv[])
{
	int	opt;
	struct termios t;

# ifdef DEBUG
	dbg = fopen(DEBUG, "w");
# endif
	while((opt = getopt(argc, argv, "h7t:g:p:ix")) != EOF)
		switch(opt) {

		  case 'h':
			usage(0);

		  case '7':
			cs7 = 1;
			break;
		
		  case 'g':
		 	geometry = optarg;
		 	break;

		  case 't':
		 	title = optarg;
		 	break;

		  case 'p':
		 	pos = optarg;
		 	break;

		  case 'i':
		 	iconic = 1;
		 	break;

		  case 'x':
		 	args = &argv[optind];
		 	goto go_on;
		}

 go_on:
	tty_init();

	if(setsid() < 0) {
		error("setsid: %s", strerror(errno));
		Exit(5);
	}

	catch_signal(SIGCHLD, onsig);

	open_pty();
	fork_xterm();

	tcgetattr(slave, &t);
	cfmakeraw(&t);
	tcsetattr(slave, TCSANOW | TCSASOFT, &t);

	tcgetattr(master, &t);
	cfmakeraw(&t);
	tcsetattr(master, TCSANOW | TCSASOFT, &t);

	close(slave);

	get_winId();

	D((dbg, "/dev/pty%s on %d/%d\n", Sc, master, slave));
	D((dbg, "xpid %d\n", xpid));

# if 0
	if(fcntl(master, F_SETFL, O_NONBLOCK)) {
		error("fcntl: can't set master to non-block: %s", strerror(errno));
		Exit(5);
	}

	if(fcntl(0, F_SETFL, O_NONBLOCK)) {
		error("fcntl: can't set sock to non-block: %s", strerror(errno));
		Exit(4);
	}
# endif

	tty_loop(master);

	return 0;
}

void
process_input()
{
}

void
process_output()
{
	if(cs7)
		makecs7();
}


void
Exit(int e)
{
	kill(xpid, 9);
	wait_child();
	exit(e);
}



/*
 * find a free pty, open both sides for r/w and
 * set globl master and slave and Sc to the 2 variable chars in the pty name
 * panic if no pty free
 */
void
open_pty(void)
{
	char	pty[] = "/dev/ptyp0";
	char	*c1, *c2 = "keep compiler happy";

	for(c1 = "pr"; *c1; c1++) {
		for(c2 = "0123456789abcdef"; *c2; c2++) {
			sprintf(pty, "/dev/pty%c%c", *c1, *c2);
			if((master = open(pty, O_RDWR)) >= 0)
			goto open_slave;
		}
	}
	error("could not open any pty\n");
	Exit(10);

 open_slave:

	sprintf(pty, "/dev/tty%c%c", *c1, *c2);
    
	if((slave = open(pty, O_RDWR)) < 0) {
		error("can't open slave: %s - %s\n", pty, strerror(errno));
		Exit(11);
	}

	Sc[0] = *c1;
	Sc[1] = *c2;
}


/*
 * fork of xterm and set global xpid to
 * xterm's pid
 * create command before fork for debugging
 */
void
fork_xterm(void)
{
	char	cmd[1000];
	int		fd;

	sprintf(cmd, "exec %s -S%s%d -geo %s%s -T '%s'%s ", _PATH_XTERM, Sc, 4,
		geometry ? geometry : "80x24",
		geometry ? "" : pos ? pos : "",
		title ? title : "p11-tty",
		iconic ? " -iconic" : "");
	while(args && *args)
		sprintf(cmd + strlen(cmd), "%s ", *args++);

	D((dbg, "starting: %s\n", cmd));

	if((xpid = fork()) < 0) {
		error("cannot fork: %s\n", strerror(errno));
		Exit(20);
	}

	if(xpid > 0)
		return;

	/* 
	 * child 
	 */
	for(fd = getdtablesize()-1; fd >= 0; fd--)
		if(fd != slave)
			close(fd);
	dup2(slave, 4);
	close(slave);

	fd = open("/", O_RDONLY);
	dup(fd);
	dup(fd);


	execl("/bin/sh", "/bin/sh", "-c", cmd, 0);
	_exit(127);
}


/*
 * xterm writes the window Id into the slave end of the
 * tty to give us a handle to control the window
 * directly
 * sets globl winId
 */
void
get_winId(void)
{
	char	wbuf[100], *p = wbuf;

	while(read(master, p, 1) == 1 && *p != '\n')
		p++;
	*p = 0;
	winId = strtol(wbuf, 0, 0x10);
}


/*
 * wait until child exited
 */
void
wait_child(void)
{
	int		status;
	pid_t	pid;

	catch_signal(SIGCHLD, SIG_DFL);
	while((pid = wait(&status)) > 0 || errno == EINTR)
		;
}

void
process_end()
{
}
