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

# include <stdio.h>
# include <stdlib.h>
# include <signal.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <sys/socket.h>
# include <sys/wait.h>
# include <errno.h>
# include <fcntl.h>
# include <unistd.h>
# include <string.h>
# include <begemot.h>

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

RCSID("$Id: tty_file.c 444 2000-11-14 12:11:23Z hbb $")


int		cs7;
int		run;
char		*fname;
char		**args;
u_int		filegen;
int		filedesc = -1;
u_char		*eofseq;
int		seqlen;
int		newfile = 0;
pid_t		child;

static char usgtxt[] =
"Begemot PDP11 emulator Version %s; Terminal-to-file adapter\n"
"Copyright (c) 1994-2000 Begemot Computer Associates. All rights reserved.\n"
"Usage: tty_file [-p pidfile] [-7] [-c] [-e seq] file [args ...]\n"
"where:\n"
"	-p file	write pid into file\n"
"	-7	mask to 7bits\n"
"	-c	run file and pipe into its stdin\n"
"	-e seq	set a EOF sequence\n";

int pipe_prog(void);
void onchld(int);
void usage(int e) DEAD_FUNC;
int next_file(void);
void parse_seq(char *);
void nextpage(int);

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


void
Exit(int ex)
{
	exit(ex);
}

int
next_file(void)
{
	char *fbuf;
	int fd;

	fbuf = xalloc(strlen(fname) + 20);
	(void)sprintf(fbuf, "%s%03u", fname, filegen++);
	if((fd = open(fbuf, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0)
		panic("%s: %s", fbuf, strerror(errno));
	free(fbuf);

	if(filedesc == -1) {
		filedesc = fd;
	} else if(filedesc != fd) {
		if(dup2(fd, filedesc) == -1)
			panic("dup2(%d,%d): %s", fd, filedesc, strerror(errno));
		(void)close(fd);
		fd = filedesc;
	}
	return fd;
}

void
parse_seq(char *arg)
{
	char *end;
	u_long val;

	while(*arg != '\0') {
		val = strtoul(arg, &end, 0);
		if(end == arg || (*end != '\0' && *end != ':'))
			panic("bad sequence at: '%s'", arg);
		if(val > 0xff)
			panic("sequence element too big 0x%lx", val);
		seqlen++;
		eofseq = xrealloc(eofseq, seqlen);
		eofseq[seqlen-1] = val;
		if(*end == ':')
			arg = end+1;
		else
			arg = end;
	}
}

int sig;

void
nextpage(int s UNUSED)
{
	sig++;
}

/*
 * fd 0 is socket to parent
 */
int
main(int argc, char *argv[])
{
	int opt;
	int fd;
	FILE *fp;

	while((opt = getopt(argc, argv, "p:e:ch7")) != EOF)
		switch(opt) {

		  case 'c':
			run = 1;
			break;

		  case 'h':
			usage(0);

		  case '7':
			cs7 = 1;
			break;

		  case 'e':
			parse_seq(optarg);
			break;

		  case 'p':
			if((fp = fopen(optarg, "w")) != NULL) {
				fprintf(fp, "%d", (int)getpid());
				fclose(fp);
			}
			break;
		}
	argc -= optind;
	argv += optind;

	catch_signal(SIGUSR1, nextpage);
	catch_signal(SIGCHLD, onchld);

	tty_init();

	fname = argv[0];
	args = &argv[0];

	if(!run) {
		fd = next_file();
	} else {
		fd = pipe_prog();
	}

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

	tty_loop(-fd);

	Exit(0);
}

void
process_output()
{
	if(cs7)
		makecs7();
	if(seqlen > 0 && got >= seqlen && memcmp(&buf[got-seqlen], eofseq, seqlen) == 0)
		newfile = 1;
}

void
process_end()
{
	if(newfile || sig) {
		if(run) {
			(void)pipe_prog();
		} else {
			(void)next_file();
		}
		if(newfile)
			newfile = 0;
		if(sig)
			sig = 0;
	}
}

void
process_input()
{
}

int
pipe_prog()
{
	int sd[2];
	int fd;

	if(child != (pid_t)0) {
		/*
		 * Close the pipe. A good child should exit.
		 */
		(void)close(filedesc);
		child = 0;
	}

	if(socketpair(PF_UNIX, SOCK_STREAM, 0, sd) < 0)
		panic("socketpair: %s", strerror(errno));

	switch(child = fork()) {

	  case -1:
		/* failure */
		panic("fork: %s", strerror(errno));

	  case 0:
		/* child */
		if(sd[0] != 0) {
			if(dup2(sd[0], 0) == -1)
				abort();
			(void)close(sd[0]);
		}

		/*
		 * don't close fds 0 (it's the pipe), 2 (stderr).
		 */
		for(fd = getdtablesize() - 1; fd > 0; fd--)
			if(fd != 2)
				(void)close(fd);
		execv(fname, args);
		panic("exec: %s - %s", fname, strerror(errno));
	}

	/* parent */
	close(sd[0]);

	if(filedesc == -1) {
		filedesc = sd[1];
	} else if(sd[1] != filedesc) {
		if(dup2(sd[1], filedesc) == -1)
			panic("dup2(%d,%d): %s", sd[1], filedesc, strerror(errno));
		(void)close(sd[1]);
		sd[1] = filedesc;
	}

	return sd[1];
}

void
onchld(int s UNUSED)
{
	pid_t pid;
	int status;

	while((int)(pid = wait(&status)) == -1)
		if(errno == ECHILD)
			break;
		else if(errno == EINTR)
			;
		else
			panic("wait: %s", strerror(errno));
}
