*****DO NOT USE FOR STRESS TESTING****** */
/*
 *
 *  This file contains Unix specific code for setting terminal modes,
 *  very little is specific to ZMODEM per se 
 *  Copyright 2005 Omen Technology INC All Rights Reserved
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <setjmp.h>
#include <ctype.h>
#include <errno.h>

#define USG

#include <sys/types.h>
#include <sys/stat.h>
#include <termio.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#define OS "SYS III/V"
#define MODE2OK
#include <string.h>

#ifndef DIAGS
#define DIAGS
#endif

#ifdef FLUSH
#define sendline(c) putchar(c)
#endif


char Pathname[1024];
long Modtime;		/* Unix style mod time for incoming file */


/* VARARGS1 */
void
vfile(f, a, b, c)
register char *f, *a;
{
	if (Verbose > 2) {
		fprintf(stderr, f, a, b, c);
		fprintf(stderr, "\n");
		fflush(stderr);
	}
}


#define HOWMANY  96
int Readnum = HOWMANY;	/* Number of bytes to ask for in read() from modem */
char linbuf[HOWMANY];
int Lleft=0;		/* number of characters in linbuf */


/*
 *  The following uses an external rdchk() routine if available,
 *  otherwise defines the function for BSD or fakes it for SYSV.
 */

#ifndef READCHECK
#ifdef FIONREAD
#define READCHECK
/*
 *  Return non 0 iff something to read from io descriptor f
 */
rdchk(f)
{
	static long lf;

	ioctl(f, FIONREAD, &lf);
	return ((int) (lf>0));
}

#else		/* FIONREAD */

#define READCHECK
#include <fcntl.h>

int checked = 0;
/*
 * Nonblocking I/O is a bit different in System V, Release 2
 *  Note: this rdchk vsn throws away a byte, OK for ZMODEM
 *  sender because protocol design anticipates this problem.
 */
rdchk(f)
{
	int lf, savestat;
	static char bchecked;

	savestat = fcntl(f, F_GETFL) ;
#ifdef O_NDELAY
	fcntl(f, F_SETFL, savestat | O_NDELAY) ;
#else
	fcntl(f, F_SETFL, savestat | O_NONBLOCK) ;
#endif
	lf = read(f, &bchecked, 1) ;
	fcntl(f, F_SETFL, savestat) ;
	checked = bchecked & 0377;	/* force unsigned byte */
	return(lf>0) ;
}
#endif
#endif


FILE *fout;
extern int errno;



#ifdef ICANON
struct termio oldtty, tty;
#else
struct sgttyb oldtty, tty;
struct tchars oldtch, tch;
#endif

int rdfd = 0;		/* Read File descriptor for ioctls & reads */
int wrfd = 1;		/* Write File descriptor for ioctls & reads */

/*
 * mode(n)
 *  3: save old tty stat, set raw mode with flow control
 *  2: set XON/XOFF for sb/sz with ZMODEM 
 *  1: save old tty stat, set raw mode 
 *  0: restore original tty mode
 */
mode(n)
{
	static did0 = FALSE;

	vfile("mode:%d", n);
	switch(n) {
#ifdef USG
	case 2:		/* Un-raw mode used by sz, sb when -g detected */
		if(!did0)
			(void) ioctl(rdfd, TCGETA, &oldtty);
		tty = oldtty;

		tty.c_iflag = BRKINT|IXON;

		tty.c_oflag = 0;	/* Transparent output */

		tty.c_cflag &= ~PARENB;	/* Disable parity */
		tty.c_cflag |= CS8;	/* Set character size = 8 */


#ifdef READCHECK
		tty.c_lflag = 0;
		tty.c_cc[VINTR] = -1;	/* Interrupt char */
#else
		tty.c_lflag = ISIG;
		tty.c_cc[VINTR] = 03;	/* Interrupt char */
#endif
		tty.c_cc[VQUIT] = -1;			/* Quit char */
#ifdef NFGVMIN
		tty.c_cc[VMIN] = 1;
#else
		tty.c_cc[VMIN] = 3;	 /* This many chars satisfies reads */
#endif
		tty.c_cc[VTIME] = 1;	/* or in this many tenths of seconds */

		(void) ioctl(rdfd, TCSETAW, &tty);
		did0 = TRUE;
		return IZOK;
	case 1:
	case 3:
		if(!did0)
			(void) ioctl(rdfd, TCGETA, &oldtty);
		tty = oldtty;

		tty.c_iflag = n==3 ? (IGNBRK|IXOFF) : IGNBRK;

		 /* No echo, crlf mapping, INTR, QUIT, delays, no erase/kill */
		tty.c_lflag &= ~(ECHO | ICANON | ISIG);

		tty.c_oflag = 0;	/* Transparent output */

		tty.c_cflag &= ~PARENB;	/* Same baud rate, disable parity */
		tty.c_cflag |= CS8;	/* Set character size = 8 */
#ifdef NFGVMIN
		tty.c_cc[VMIN] = 1; /* This many chars satisfies reads */
#else
		tty.c_cc[VMIN] = HOWMANY; /* This many chars satisfies reads */
#endif
		tty.c_cc[VTIME] = 1;	/* or in this many tenths of seconds */
		(void) ioctl(rdfd, TCSETAW, &tty);
		did0 = TRUE;
		return IZOK;
#endif
#ifdef V7
	/*
	 *  NOTE: this should transmit all 8 bits and at the same time
	 *   respond to XOFF/XON flow control.  If no FIONREAD or other
	 *   READCHECK alternative, also must respond to INTRRUPT char
	 *   This doesn't work with V7.  It should work with LLITOUT,
	 *   but LLITOUT was broken on the machine I tried it on.
	 */
	case 2:		/* Un-raw mode used by sz, sb when -g detected */
		if(!did0) {
			ioctl(rdfd, TIOCEXCL, 0);
			ioctl(rdfd, TIOCGETP, &oldtty);
			ioctl(rdfd, TIOCGETC, &oldtch);
#ifdef LLITOUT
			ioctl(rdfd, TIOCLGET, &Locmode);
#endif
		}
		tty = oldtty;
		tch = oldtch;
#ifdef READCHECK
		tch.t_intrc = -1;	/* Interrupt char */
#else
		tch.t_intrc = 03;	/* Interrupt char */
#endif
		tty.sg_flags |= (ODDP|EVENP|CBREAK);
		tty.sg_flags &= ~(ALLDELAY|CRMOD|ECHO|LCASE);
		ioctl(rdfd, TIOCSETP, &tty);
		ioctl(rdfd, TIOCSETC, &tch);
#ifdef LLITOUT
		ioctl(rdfd, TIOCLBIS, &Locbit);
#endif
		bibi(99);	/* un-raw doesn't work w/o lit out */
		did0 = TRUE;
		return IZOK;
	case 1:
	case 3:
		if(!did0) {
			ioctl(rdfd, TIOCEXCL, 0);
			ioctl(rdfd, TIOCGETP, &oldtty);
			ioctl(rdfd, TIOCGETC, &oldtch);
#ifdef LLITOUT
			ioctl(rdfd, TIOCLGET, &Locmode);
#endif
		}
		tty = oldtty;
		tty.sg_flags |= RAW;
		tty.sg_flags &= ~ECHO;
		ioctl(rdfd, TIOCSETP, &tty);
		did0 = TRUE;
		return IZOK;
#endif
	case 0:
		if(!did0)
			return IZERROR;
#ifdef USG
		(void) ioctl(wrfd, TCSBRK, 1);	/* Wait for output to drain */
		(void) ioctl(rdfd, TCFLSH, 1);	/* Flush input queue */
		(void) ioctl(rdfd, TCSETAW, &oldtty);	/* Restore modes */
		(void) ioctl(wrfd, TCXONC,1);	/* Restart output */
#endif
#ifdef V7
		ioctl(rdfd, TIOCSETP, &oldtty);
		ioctl(rdfd, TIOCSETC, &oldtch);
		ioctl(rdfd, TIOCNXCL, 0);
#ifdef LLITOUT
		ioctl(rdfd, TIOCLSET, &Locmode);
#endif
#endif

		return IZOK;
	default:
		return IZERROR;
	}
}

void
sendbrk()
{
#ifdef V7
#ifdef TIOCSBRK
#define CANBREAK
	while (readline(1) > 0)
		;
	ioctl(wrfd, TIOCSBRK, 0);
	while (readline(1) > 0)
		;
	ioctl(wrfd, TIOCCBRK, 0);
#endif
#endif
#ifdef USG
#define CANBREAK
	ioctl(wrfd, TCSBRK, 0);
#endif
}

jmp_buf tohere;		/* For the interrupt on RX timeout */

void alrm(c)
{
	longjmp(tohere, -1);
}

/*
 * This version of readline is reasoably well suited for
 * reading many characters.
 *  (except, currently, for the Regulus version!)
 *
 * timeout is in tenths of seconds
 */
readline(timeout)
int timeout;
{
	register n;
	static char *cdq;	/* pointer for removing chars from linbuf */

	if (--Lleft >= 0) {
		if (Verbose > 8) {
			fprintf(stderr, "%02x ", *cdq&0377);
			fflush(stderr);
		}
		return (*cdq++ & 0377);
	}
	n = timeout/10;
	if (n < 2)
		n = 2;
	if (Verbose > 5) {
		fprintf(stderr, "Calling read: alarm=%d  Readnum=%d ", n, Readnum);
		fflush(stderr);
	}
	if (setjmp(tohere)) {
		Lleft = 0;
		if (Verbose>1) {
			fprintf(stderr, "Readline:TIMEOUT\n");
			fflush(stderr);
		}
		return TIMEOUT;
	}
	signal(SIGALRM, alrm); alarm(n);
	alarm(n);
	alarm(n);
	alarm(n);
	alarm(n);
	Lleft=read(rdfd, cdq=linbuf, Readnum);
	alarm(0);
	if (Verbose > 5) {
		fprintf(stderr, "Read returned %d bytes\n", Lleft);
		fflush(stderr);
	}
	if (Lleft < 1)
		return TIMEOUT;
	--Lleft;
	if (Verbose > 8) {
		fprintf(stderr, "%02x ", *cdq&0377);
		fflush(stderr);
	}
	return (*cdq++ & 0377);
}

checkline()
{
	if (Lleft > 0)
		return 1;
	return (rdchk(rdfd));
}

void
flushmoc()
{
	fflush(stdout);
}

void
flushmo()
{
	fflush(stdout);
}

/*
 * Purge the modem input queue of all characters
 */
void
purgeline()
{
	Lleft = 0;
	while (readline(1) > 0)
		;
//	ioctl(rdfd, TCFLSH, 0);
//	lseek(rdfd, 0, SEEK_END);
}


/*
 * Putsec writes the n characters of buf to receive file fout.
 *  This code is specific to Unix like systems!!!
 */
void
putsec(buf, n)
char *buf;
register n;
{
	register char *p;

	if (n == 0)
		return;
	for (p=buf; --n>=0; )
		putc( *p++, fout);
}

#ifndef FLUSH
/*
 *  Send a character to modem.  Small is beautiful.
 */
void
sendline(c)
{
	char d;

	d = c;
	if (Verbose>9) {
		fprintf(stderr, "Sendline: %x\n", c & 0377);
		fflush(stderr);
	}
	write(wrfd, &d, 1);
}
#endif

/*
 * Log an error
 */
/*VARARGS1*/
void
zperr(s,p,u)
char *s, *p, *u;
{
	if (Verbose <= 0)
		return;
	fprintf(stderr, s, p, u);
	fprintf(stderr, "\n");
	fflush(stderr);
}
void
zbitch(s)
char *s;
{
	if (Verbose <= 0)
		return;
	fprintf(stderr, s);
	fprintf(stderr, "\n");
	fflush(stderr);
}


/*
 * Close the receive dataset
 */
void
closeit(status)
{
	time_t timep[2];

	fclose(fout);
	if (Modtime) {
		timep[0] = time(NULL);
		timep[1] = Modtime;
		utime(Pathname, timep);
	}
}
