#define	__TOWNS__
//#define	DEBUG
#define VERSION "3.11 02-26-91"
#define PUBDIR "/usr/spool/uucppublic"

#ifdef	__TOWNS__
#	define	TXBSIZE	16384
#	define	READCHECK
#endif

/*% cc -compat -M2 -Ox -K -i -DTXBSIZE=16384  -DNFGVMIN -DREADCHECK sz.c -lx -o sz; size sz

<-xtx-*> cc -Osal -DTXBSIZE=32768  -DSV sz.c -lx -o $B/sz; size $B/sz

 ****************************************************************************
 *
 * sz.c By Chuck Forsberg,  Omen Technology INC
 *
 ****************************************************************************
 *
 * Typical Unix/Xenix/Clone compiles:
 *
 *		cc -O sz.c -o sz				USG (SYS III/V) Unix
 *		cc -O -DSV sz.c -o sz			Sys V Release 2 with non-blocking input
 *										Define to allow reverse channel checking
 *		cc -O -DV7  sz.c -o sz			Unix Version 7, 2.8 - 4.3 BSD
 *
 *		cc -O -K -i -DNFGVMIN -DREADCHECK sz.c -lx -o sz		Classic Xenix
 *
 *		ln sz sb						**** All versions ****
 *		ln sz sx						**** All versions ****
 *
 ****************************************************************************
 ****************************************************************************
 *
 *
 * A program for Unix to send files and commands to computers running
 *  Professional-YAM, PowerCom, YAM, IMP, or programs supporting Y/XMODEM.
 *
 *  Sz uses buffered I/O to greatly reduce CPU time compared to UMODEM.
 *
 *  USG UNIX (3.0) ioctl conventions courtesy Jeff Martin
 *
 * 
 *		This version implements numerous enhancements including ZMODEM
 *		Run Length Encoding and variable length headers.  These
 *		features were not funded by the original Telenet development
 *		contract.
 * 
 * This software may be freely used for non commercial and
 * educational (didactic only) purposes.  This software may also
 * be freely used to support file transfer operations to or from
 * licensed Omen Technology products.  Any programs which use
 * part or all of this software must be provided in source form
 * with this notice intact except by written permission from Omen
 * Technology Incorporated.
 * 
 * Use of this software for commercial or administrative purposes
 * except when exclusively limited to interfacing Omen Technology
 * products requires a per port license payment of $20.00 US per
 * port (less in quantity).  Use of this code by inclusion,
 * decompilation, reverse engineering or any other means
 * constitutes agreement to these conditions and acceptance of
 * liability to license the materials and payment of reasonable
 * legal costs necessary to enforce this license agreement.
 *
 *
 *				Omen Technology Inc				FAX: 503-621-3745
 *				Post Office Box 4681
 *				Portland OR 97208
 *
 *		This code is made available in the hope it will be useful,
 *		BUT WITHOUT ANY WARRANTY OF ANY KIND OR LIABILITY FOR ANY
 *		DAMAGES OF ANY KIND.
 */


#ifndef	__TOWNS__
char	   *substr(), *getenv();
#endif

#define LOGFILE "/tmp/szlog"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <stat.h>
#include <setjmp.h>
#include <ctype.h>
#include <errno.h>

#ifdef	__TOWNS__
#	include	<stdarg.h>
#	include	<process.h>
#	include	<time.h>
#	include	<io.h>
#	include	<splib.h>
#	include	<fslib.h>

#	include	"usrlib.h"
#	include	"rsctrl.h"
#	include	"flib.h"
#	include	"msgdat.h"

#	ifdef	__HIGHC__
#		pragma	On(Align_labels);
#	endif

	extern	int	RsPort;
#	define	_FSTAT_IGN
#endif

#include	"prot.h"
#include	"sz.h"

extern	int		errno;
#define	STATIC	static

#define HOWMANY 2
STATIC int		Zmodem = 0; 		/* ZMODEM protocol requested by receiver */
unsigned		Baudrate = 4800;	/* Default, set by first mode() call */
unsigned		Effbaud = 4800;
STATIC unsigned Txwindow;			/* Control the size of the transmitted window */
STATIC unsigned Txwspac;			/* Spacing between zcrcq requests */
STATIC unsigned Txwcnt; 			/* Counter used to space ack requests */
STATIC long		Lrxpos; 			/* Receiver's last reported offset */
STATIC int		errors;
char			endmsg[80] = {0};	/* Possible message to display on exit */

#include "rbsb.c"					/* most of the system dependent stuff here */
#include "crctab.c"

STATIC int	Filesleft;
STATIC long	Totalleft;

/*
 * Attention string to be executed by receiver to interrupt streaming data
 *  when an error is detected.  A pause (0336) may be needed before the
 *  ^C (03) or after it.
 */
#ifdef READCHECK
	STATIC char Myattn[] = {0};
#else
#	ifdef USG
		STATIC char Myattn[] = {03, 0336, 0};
#	endif
#endif

FILE	   *in;
char	   *infile;	/* High C stat() */

#ifdef BADSEEK
STATIC int	Canseek = 0;	/* 1: Can seek 0: only rewind -1: neither (pipe) */
#	ifndef TXBSIZE
#		define TXBSIZE 16384	/* Must be power of two, < MAXINT */
#	endif
#else
STATIC int	Canseek = 1;		/* 1: Can seek 0: only rewind -1: neither (pipe) */
#endif

#ifdef TXBSIZE
#define TXBMASK (TXBSIZE-1)
STATIC char Txb[TXBSIZE];		/* Circular buffer for file reads */
STATIC char *txbuf = Txb;		/* Pointer to current file segment */
#else
STATIC char txbuf[1024];
#endif
STATIC long vpos = 0;			/* Number of bytes read from file */

STATIC char Lastrx;
STATIC char Crcflg;
#ifdef	DEBUG
STATIC int	Verbose = 5;
#else
STATIC int	Verbose = 0;
#endif
STATIC int	Modem2 = 0; 		/* XMODEM Protocol - don't send pathnames */
STATIC int	Restricted = 0; 	/* restricted; no /.. or ../ in filenames */
STATIC int	Quiet = 0;			/* overrides logic that would otherwise set verbose */
STATIC int	Ascii = 0;			/* Add CR's for brain damaged programs */
STATIC int	Fullname = 0;		/* transmit full pathname */
STATIC int	Unlinkafter = 0;	/* Unlink file after it is sent */
STATIC int	Dottoslash = 0; 	/* Change foo.bar.baz to foo/bar/baz */
STATIC int	firstsec;
STATIC int	errcnt = 0; 		/* number of files unreadable */
STATIC int	blklen = 128;		/* length of transmitted records */
STATIC int	Optiong;			/* Let it rip no wait for sector ACK's */
STATIC int	Eofseen;			/* EOF seen on input set by zfilbuf */
STATIC int	BEofseen;			/* EOF seen on input set by fooseek */
STATIC int	Totsecs;			/* total number of sectors this file */
STATIC int	Filcnt = 0; 		/* count of number of files opened */
STATIC int	Lfseen = 0;
STATIC unsigned Rxbuflen = 16384;	/* Receiver's max buffer length */
STATIC int	Tframlen = 0;		/* Override for tx frame length */
STATIC int	blkopt = 0; 		/* Override value for zmodem blklen */
STATIC int	Rxflags = 0;
STATIC long bytcnt;
STATIC int	Wantfcs32 = TRUE;	/* want to send 32 bit FCS */
STATIC char Lzconv; 			/* Local ZMODEM file conversion request */
STATIC char Lzmanag;			/* Local ZMODEM file management request */
STATIC int	Lskipnocor;
STATIC char Lztrans;
STATIC int	Command;			/* Send a command, then exit. */
STATIC char *Cmdstr;			/* Pointer to the command string */
STATIC int	Cmdtries = 11;
STATIC int	Cmdack1;			/* Rx ACKs command, then do it */
STATIC int	Exitcode;
STATIC int	Test = 0;			/* 1= Force receiver to send Attn, etc with qbf.
                                   2= Character transparency test */
STATIC char *qbf = "The quick brown fox jumped over the lazy dog's back 1234567890\r\n";
STATIC long Lastsync = 0;		/* Last offset to which we got a ZRPOS */
STATIC int	Beenhereb4 = 0; 	/* How many times we've been ZRPOS'd same place */

STATIC jmp_buf tohere;			/* For the interrupt on RX timeout */
STATIC jmp_buf intrjmp; 		/* For the interrupt on RX CAN */

#ifdef XARGSFILE
static	char	   *mystrsave(char *s)
{
	register char *p;
	char	   *malloc();

	if (p = malloc(strlen(s) + 1))
	{
		strcpy(p, s);
		return p;
	}
	USR_fprintf(stderr, "No memory for mystrsave!\n");
	EXIT(1);
}

/* Remove (presumably) terminating CR and/or LF from string */
static	void	uncrlf( register char *s )
{
	for (; *s; ++s)
		switch (*s)
		{
			case '\r':
			case '\n':
				*s = 0;
				return;
		}
}
#endif


#ifdef	__TOWNS__

void	(*signal_int_func)(int) = SIG_DFL;
extern	int		CtrlX;

void	signal_func(int level)
{
	if ( signal_int_func == SIG_ERR )
	{
	} else if ( signal_int_func == SIG_DFL )
	{
		EXIT(3);
	} else if ( signal_int_func == SIG_IGN )
	{
	} else
		(signal_int_func)(level);
}

void	my_signal( int mode, void (*func)(int) )
{
	switch ( mode )
	{
		case SIGINT:
			signal_int_func = func;
			break;
	}
}
#define	SIGNAL(_mode,_func)	my_signal(_mode,_func)

/* called by signal interrupt or terminate to clean things up */
void	bibi(int n)
{
	SIGNAL(SIGINT, SIG_IGN);
	canit();
	mode(0);	/* restore original tty mode */
	USR_fprintf(stderr, "sz: caught signal %d; exiting\n", n);
	if (n == SIGQUIT)
		EXIT(0);
	if (n == 99)
		USR_fprintf(stderr, "mode(2) in rbsb.c not implemented!!\n");
	cucheck();
	EXIT(128 + n);
}
/* Called when ZMODEM gets an interrupt (^X) */
void	onintr(int level)
{
	SIGNAL(SIGINT, SIG_IGN);
	CtrlX = FALSE;
	longjmp(intrjmp, -1);
}
#else
/* called by signal interrupt or terminate to clean things up */
void	bibi(int n)
{
	canit();
	mode(0);	/* restore original tty mode */
	USR_fprintf(stderr, "sz: caught signal %d; exiting\n", n);
	if (n == SIGQUIT)
		abort();
	if (n == 99)
		USR_fprintf(stderr, "mode(2) in rbsb.c not implemented!!\n");
	cucheck();
	EXIT(128 + n);
}
/* Called when ZMODEM gets an interrupt (^X) */
void	onintr(void)
{
	signal(SIGINT, SIG_IGN);
	longjmp(intrjmp, -1);
}
#endif


STATIC int	Zctlesc;			/* Encode control characters */
STATIC int	Nozmodem = 0;		/* If invoked as "sb" */
STATIC char *Progname = "sz";
STATIC int	Zrwindow = 1400;	/* RX window size (controls garbage count) */
#include "zm.c"

#include "zmr.c"

#ifdef XARGSFILE
#define XARGSMAX 256
char	*xargv[XARGSMAX + 1];
#endif

void	zm_main(int argc, char *argv[])
{
	register char *cp;
	register	npats;
	int 		dm;
	char	  **patts;
//	static char xXbuf[BUFSIZ];

#ifndef	__TOWNS__
	if ((cp = getenv("ZNULLS")) && *cp)
		Znulls = atoi(cp);
	if ((cp = getenv("SHELL")) && (substr(cp, "rsh") || substr(cp, "rksh")))
		Restricted = TRUE;
#endif
	from_cu();
	chkinvok(argv[0]);

	Rxtimeout = 600;
	npats = 0;
	if (argc < 2)
		usage();
#ifndef	__TOWNS__
	setbuf(stdout, xXbuf);
#endif
	while ( --argc )
	{
		cp = *++argv;
		if (*cp++ == '-' && *cp)
		{
			while (*cp)
			{
				switch (*cp++)
				{
					case '\\':
						*cp = toupper(*cp);
						continue;
					case '+':
						Lzmanag = ZMAPND;
						break;
#ifdef CSTOPB
					case '2':
						Twostop = TRUE;
						break;
#endif
					case 'a':
						Lzconv = ZCNL;
						Ascii = TRUE;
						break;
					case 'b':
						Lzconv = ZCBIN;
						break;
					case 'C':
						if (--argc < 1)
						{
							usage();
						}
						Cmdtries = atoi(*++argv);
						break;
					case 'i':
						Cmdack1 = ZCACK1;
						/* **** FALL THROUGH TO **** */
					case 'c':
						if (--argc != 1)
						{
							usage();
						}
						Command = TRUE;
						Cmdstr = *++argv;
						break;
					case 'd':
						++Dottoslash;
						/* **** FALL THROUGH TO **** */
					case 'f':
						Fullname = TRUE;
						break;
					case 'e':
						Zctlesc = 1;
						break;
					case 'k':
						blklen = 1024;
						break;
					case 'L':
						if (--argc < 1)
						{
							usage();
						}
						blkopt = atoi(*++argv);
						if (blkopt < 24 || blkopt > 1024)
							usage();
						break;
					case 'l':
						if (--argc < 1)
						{
							usage();
						}
						Tframlen = atoi(*++argv);
						if (Tframlen < 32 || Tframlen > 1024)
							usage();
						break;
					case 'N':
						Lzmanag = ZMNEWL;
						break;
					case 'n':
						Lzmanag = ZMNEW;
						break;
					case 'o':
						Wantfcs32 = FALSE;
						break;
					case 'p':
						Lzmanag = ZMPROT;
						break;
					case 'r':
						if (Lzconv == ZCRESUM)
							Lzmanag = (Lzmanag & ZMMASK) | ZMCRC;
						Lzconv = ZCRESUM;
						break;
					case 'q':
						Quiet = TRUE;
						Verbose = 0;
						break;
					case 't':
						if (--argc < 1)
						{
							usage();
						}
						Rxtimeout = atoi(*++argv);
						if (Rxtimeout < 10 || Rxtimeout > 1000)
							usage();
						break;
					case 'T':
						if (++Test > 1)
						{
							vfile("test mode");
							chartest(1);	/* save old tty stat, set raw mode */
							chartest(2);	/* set XON/XOFF for sb/sz with ZMODEM or YMODEM-g */
							mode(0);		/* restore original tty mode */
							EXIT(0);
						}
						break;
					case 'u':
						++Unlinkafter;
						break;
					case 'v':
						++Verbose;
						break;
					case 'w':
						if (--argc < 1)
						{
							usage();
						}
						Txwindow = atoi(*++argv);
						if (Txwindow < 256)
							Txwindow = 256;
						Txwindow = (Txwindow / 64) * 64;
						Txwspac = Txwindow / 4;
						if (blkopt > Txwspac
							|| (!blkopt && Txwspac < 1024))
							blkopt = Txwspac;
						break;
					case 'X':
						++Modem2;
						break;
					case 'Y':
						Lskipnocor = TRUE;
						/* **** FALLL THROUGH TO **** */
					case 'y':
						Lzmanag = ZMCLOB;
						break;
					case 'Z':
					case 'z':
						Lztrans = ZTRLE;
						break;
					default:
						usage();
				}
			}
		} else if (!npats && argc > 0)
		{
			if (argv[0][0])
			{
				npats = argc;
				patts = argv;
			}
		}
	}
	if (npats < 1 && !Command && !Test)
		usage();
#ifndef	__TOWNS__
	if ( Verbose )
	{
		if ( freopen(LOGFILE, "a", stderr) == NULL )
		{
			printf("Can't open log file %s\n", LOGFILE);
			EXIT(0200);
		}
		setbuf(stderr, NULL);
	}
#endif
	if (Fromcu && !Quiet)
	{
		if (Verbose == 0)
			Verbose = 2;
	}
#ifdef	__TOWNS__
	USR_fprintf(stderr,"\n");
	USR_fprintf(stderr,"sz ver.%s for TownsOS", MAIN_VER);
	USR_fprintf(stderr,"    orignal sz %s\n\n", VERSION);
#else
	vfile("%s %s for %s\n", Progname, VERSION, OS);
#endif

#ifdef XARGSFILE
	vfile("npats=%d *patts=%s", npats, *patts);
	if (npats == 1 && !strcmp(XARGSFILE, *patts))
	{
		infile = XARGSFILE;	in = FM_fopen(infile, "rb");	
		if (!in)
		{
			USR_printf(stderr, "Can't open / control file!\n");
			EXIT(2);
		}
		for (npats = 0, argv = patts = xargv; npats < XARGSMAX; ++npats, ++argv)
		{
			if (fgets(txbuf, 1024, in) <= 0)
				break;
			uncrlf(txbuf);
			*argv = mystrsave(txbuf);
		}
		FM_fclose(in);
	}
#endif

	mode(1);	/* save old tty stat, set raw mode */

#ifdef	__TOWNS__
	SIGNAL(SIGINT,bibi);
#else
	if (signal(SIGINT, bibi) == SIG_IGN)
	{
		signal(SIGINT, SIG_IGN);
		signal(SIGKILL, SIG_IGN);
	} else
	{
		signal(SIGINT, bibi);
		signal(SIGKILL, bibi);
	}
#ifdef SIGQUIT
	if (!Fromcu)
		signal(SIGQUIT, SIG_IGN);
#endif
#ifdef SIGTERM
	signal(SIGTERM, bibi);
#endif
#endif /* endof ifndef __TOWNS__ */

	if (!Modem2)
	{
		if (!Nozmodem)
		{
			RS_puts(RsPort,"rz\r");
		}
		countem(npats, patts);
		if (!Nozmodem)
		{
			stohdr(0L);
			if (Command)
				Txhdr[ZF0] = ZCOMMAND;
			zshhdr(4, ZRQINIT, Txhdr);
		}
	}

	if (Command)
	{
		if (getzrxinit())
		{
			Exitcode = 0200;
			canit();
		} else if (zsendcmd(Cmdstr, 1 + strlen(Cmdstr)))
		{
			Exitcode = 0200;
			canit();
		}
	} else if (wcsend(npats, patts) == ERROR)
	{
		Exitcode = 0200;
		canit();
	}
	if ( endmsg[0] )
	{
#ifdef	DEBUG
		USR_printf("\n%s\n", endmsg);
#endif
		RS_printf(RsPort,"\r\n%s", endmsg);
	}

#ifdef	__TOWNS__
	USR_fprintf(stderr,"\nrx ver.%s finished.\n",MAIN_VER);
#else
	RS_printf(RsPort,"\r\n%s %s finished.\r\n", Progname, VERSION);
#endif
	mode(0);	/* restore original tty mode */
	dm = ((errcnt != 0) | Exitcode);
	if (dm)
		cucheck();
	EXIT(dm);
	/* NOTREACHED */
}

static	int		wcsend(int argc, char *argp[])
{
	register	n;

	Crcflg = FALSE;
	firstsec = TRUE;
	bytcnt = -1;
	if (Nozmodem)
	{
		RS_puts(RsPort,"Start your YMODEM receive. ");
	}
	for (n = 0; n < argc; ++n)
	{
		Totsecs = 0;
		if (wcs(argp[n]) == ERROR)
			return ERROR;
	}
	Totsecs = 0;
	if (Filcnt == 0)
	{							/* bitch if we couldn't open ANY files */
		if (!Nozmodem && !Modem2)
		{
			Command = TRUE;
			Cmdstr = "echo \"sz: Can't open any requested files\"";
			if (getnak())
			{
				Exitcode = 0200;
				canit();
			}
			if (!Zmodem)
				canit();
			else if (zsendcmd(Cmdstr, 1 + strlen(Cmdstr)))
			{
				Exitcode = 0200;
				canit();
			}
			Exitcode = 1;
			return OK;
		}
		canit();
		sprintf(endmsg, "Can't open any requested files");
		return ERROR;
	}
	if (Zmodem)
		saybibi();
	else if (!Modem2)
		wctxpn("");
	return OK;
}

static	int		wcs(char *oname)
{
//	register char *p, *q;
	struct stat f;
	char		name[PATHLEN];

	strcpy(name, oname);

	if (Restricted)
	{
		/* restrict pathnames to current tree or uucppublic */
		if (substr(name, "../")
			|| (name[0] == '/' && strncmp(name, PUBDIR, strlen(PUBDIR))) )
		{
			canit();
			sprintf(endmsg, "Security Violation");
			return ERROR;
		}
	}
	infile = oname;	in = FM_fopen(oname, ROPMODE);

	if (in == NULL)
	{
		++errcnt;
		return OK;				/* pass over it, there may be others */
	}
	BEofseen = Eofseen = 0;
	vpos = 0;

	/* Check for directory or block special files */
#ifdef	_FSTAT_IGN
	stat(infile, &f);
#else
	fstat(fileno(in), &f);
#endif
#ifndef	__TOWNS__
	{
		register	c;

		c = f.st_mode & S_IFMT;
		if (c == S_IFDIR || c == S_IFBLK)
		{
			FM_fclose(in);
			return OK;
		}
	}
#endif
	++Filcnt;
	switch (wctxpn(name))
	{
		case ERROR:
			return ERROR;
		case ZSKIP:
			return OK;
	}
	if (!Zmodem && wctx(f.st_size) == ERROR)
		return ERROR;

	if (Unlinkafter)
		unlink(oname);

	return 0;
}

/*
 * generate and transmit pathname block consisting of pathname (null
 * terminated), file length, mode time and file mode in octal as provided by
 * the Unix fstat call. N.B.: modifies the passed name, may extend it!
 */
static	int		wctxpn(char *name)
{
	register char *p, *q;
	char		name2[PATHLEN];
	struct stat f;

#ifdef	__TOWNS__
	char	tmp[PATHLEN];
	FS_dos2unix( tmp, name );
	if ( !Fullname && isalpha(tmp[0]) && tmp[1] == ':' )
		name = &tmp[2];
	else
		name = tmp;
#endif

	if (Modem2)
	{
#ifdef	_FSTAT_IGN
		if (*name && stat(infile, &f) != -1)
#else
		if (*name && fstat(fileno(in), &f) != -1)
#endif
		{
			USR_fprintf(stderr, "Sending %s, %ld XMODEM blocks. ",
					name, (127 + f.st_size) >> 7);
		}
		USR_fprintf(stderr, "Give your local XMODEM receive command now.\r\n");
		return OK;
	}
	zperr("Awaiting pathname nak for %s", *name ? name : "<END>");
	if (!Zmodem)
	{
		if (getnak())
			return ERROR;
	}

	q = (char *) 0;
	if (Dottoslash)
	{							/* change . to . */
		for (p = name; *p; ++p)
		{
			if (*p == '/')
				q = p;
			else if (*p == '.')
				*(q = p) = '/';
		}
		if (q && strlen(++q) > 8)
		{						/* If name>8 chars */
			q += 8; 			/* make it .ext */
			strcpy(name2, q);	/* save excess of name */
			*q = '.';
			strcpy(++q, name2); /* add it back */
		}
	}
	for (p = name, q = txbuf; *p;)
	{
		if ((*q++ = *p++) == '/' && !Fullname)
			q = txbuf;
	}
	*q++ = 0;
	p = q;
	while (q < (txbuf + 1024))
		*q++ = 0;
	if (*name)
	{
#ifdef	_FSTAT_IGN
		if ( stat(infile, &f) != -1 )
#else
		if ( fstat(fileno(in), &f) != -1 )
#endif
			sprintf(p, "%lu %lo %o 0 %d %ld", f.st_size, f.st_mtime,
					f.st_mode, Filesleft, Totalleft);
		USR_fprintf(stderr,"\nSend header : %s\n", p );
		Totalleft -= f.st_size;
	}
	if (--Filesleft <= 0)
		Totalleft = 0;
	if (Totalleft < 0)
		Totalleft = 0;

	/* force 1k blocks if name won't fit in 128 byte block */
	if (txbuf[125])
		blklen = 1024;
	else
	{							/* A little goodie for IMP/KMD */
		txbuf[127] = (f.st_size + 127) >> 7;
		txbuf[126] = (f.st_size + 127) >> 15;
	}
	if (Zmodem)
		return zsendfile(txbuf, 1 + strlen(p) + (p - txbuf));
	if (wcputsec(txbuf, 0, 128) == ERROR)
		return ERROR;
	return OK;
}

static	int		getnak(void)
{
	register	firstch;

	Lastrx = 0;
	for (;;)
	{
		switch (firstch = readline(800))
		{
			case ZPAD:
				if (getzrxinit())
					return ERROR;
				Ascii = 0;		/* Receiver does the conversion */
				return FALSE;
			case TIMEOUT:
				sprintf(endmsg, "Timeout waiting for ZRINIT");
				return TRUE;
			case WANTG:
#ifdef MODE2OK
				mode(2);		/* Set cbreak, XON/XOFF, etc. */
				/* set XON/XOFF for sb/sz with ZMODEM or YMODEM-g */
#endif
				Optiong = TRUE;
				blklen = 1024;
			case WANTCRC:
				Crcflg = TRUE;
			case NAK:
				return FALSE;
			case CAN:
				if ((firstch = readline(20)) == CAN && Lastrx == CAN)
				{
					sprintf(endmsg, "Got CAN waiting to send file");
					return TRUE;
				}
			default:
				break;
		}
		Lastrx = firstch;
	}
}


static	int		wctx(long flen)
{
	register int thisblklen;
	register int sectnum, attempts, firstch;
	long		charssent;

	charssent = 0;
	firstsec = TRUE;
	thisblklen = blklen;
	vfile("wctx:file length=%ld", flen);

	while ((firstch = readline(Rxtimeout)) != NAK && firstch != WANTCRC
		   && firstch != WANTG && firstch != TIMEOUT && firstch != CAN)
		;
	if (firstch == CAN)
	{
		zperr("Receiver CANcelled");
		return ERROR;
	}
	if (firstch == WANTCRC)
		Crcflg = TRUE;
	if (firstch == WANTG)
		Crcflg = TRUE;
	sectnum = 0;
	for (;;)
	{
		if (flen <= (charssent + 896L))
			thisblklen = 128;
		if (!filbuf(txbuf, thisblklen))
			break;
		if (wcputsec(txbuf, ++sectnum, thisblklen) == ERROR)
			return ERROR;
		charssent += thisblklen;
	}
	FM_fclose(in);
	attempts = 0;
	do
	{
		purgeline();
		sendline(EOT);
		flushmo();
		++attempts;
	}
	while ( ((firstch = (readline(Rxtimeout))) != ACK) && attempts < RETRYMAX);
	if (attempts == RETRYMAX)
	{
		zperr("No ACK on EOT");
		return ERROR;
	} else
		return OK;
}

/* data length of this sector to send */
static	int		wcputsec(char *buf, int sectnum, int cseclen)
{
	register	checksum, wcj;
	register char *cp;
	unsigned	oldcrc;
	int 		firstch;
	int 		attempts;

	firstch = 0;				/* part of logic to detect CAN CAN */

	if (Verbose > 2)
		USR_fprintf(stderr, "Sector %3d %2dk\n", Totsecs, Totsecs / 8);
	else if (Verbose > 1)
		USR_fprintf(stderr, "\rSector %3d %2dk ", Totsecs, Totsecs / 8);
	for (attempts = 0; attempts <= RETRYMAX; attempts++)
	{
		Lastrx = firstch;
		sendline(cseclen == 1024 ? STX : SOH);
		sendline(sectnum);
		sendline(-sectnum - 1);
		oldcrc = checksum = 0;
		for (wcj = cseclen, cp = buf; --wcj >= 0;)
		{
			sendline(*cp);
			oldcrc = updcrc((0377 & *cp), oldcrc);
			checksum += *cp++;
		}
		if (Crcflg)
		{
			oldcrc = updcrc(0, updcrc(0, oldcrc));
			sendline((int) oldcrc >> 8);
			sendline((int) oldcrc);
		} else
			sendline(checksum);
		flushmo();

		if (Optiong)
		{
			firstsec = FALSE;
			return OK;
		}
		firstch = readline(Rxtimeout);
	  gotnak:
		switch (firstch)
		{
			case CAN:
				if (Lastrx == CAN)
				{
				  cancan:
					zperr("Cancelled");
					return ERROR;
				}
				break;
			case TIMEOUT:
				zperr("Timeout on sector ACK");
				continue;
			case WANTCRC:
				if (firstsec)
					Crcflg = TRUE;
			case NAK:
				zperr("NAK on sector");
				continue;
			case ACK:
				firstsec = FALSE;
				Totsecs += (cseclen >> 7);
				return OK;
			case ERROR:
				zperr("Got burst for sector ACK");
				break;
			default:
				zperr("Got %02x for sector ACK", firstch);
				break;
		}
		for (;;)
		{
			Lastrx = firstch;
			if ((firstch = readline(Rxtimeout)) == TIMEOUT)
				break;
			if (firstch == NAK || firstch == WANTCRC)
				goto gotnak;
			if (firstch == CAN && Lastrx == CAN)
				goto cancan;
		}
	}
	zperr("Retry Count Exceeded");
	return ERROR;
}

/* fill buf with count chars padding with ^Z for CPM */
static	int		filbuf(register char *buf, int count)
{
	register	c, m;

	if (!Ascii)
	{
#ifndef	__TOWNS__
		m = read(fileno(in), buf, count);
#else
		m = fread(buf,1,count,in);
#endif
		if (m <= 0)
			return 0;
		while (m < count)
			buf[m++] = 032;
		return count;
	}
	m = count;
	if (Lfseen)
	{
		*buf++ = 012;
		--m;
		Lfseen = 0;
	}
	while ((c = getc(in)) != EOF)
	{
		if (c == 012)
		{
			*buf++ = 015;
			if (--m == 0)
			{
				Lfseen = TRUE;
				break;
			}
		}
		*buf++ = c;
		if (--m == 0)
			break;
	}
	if (m == count)
		return 0;
	else
		while (--m >= 0)
			*buf++ = CPMEOF;
	return count;
}

/* Fill buffer with blklen chars */
static	int		zfilbuf(void)
{
	int 		n;

#ifdef TXBSIZE
	vfile("zfilbuf: bytcnt =%lu vpos=%lu blklen=%d", bytcnt, vpos, blklen);
	/* We assume request is within buffer, or just beyond */
	txbuf = Txb + (bytcnt & TXBMASK);
	if (vpos <= bytcnt)
	{
		n = fread(txbuf, 1, blklen, in);

		vpos += n;
		if (n < blklen)
			Eofseen = 1;
		vfile("zfilbuf: n=%d vpos=%lu Eofseen=%d", n, vpos, Eofseen);
		return n;
	}
	if (vpos >= (bytcnt + blklen))
		return blklen;
	/* May be a short block if crash recovery etc. */
	Eofseen = BEofseen;
	return (vpos - bytcnt);
#else
	n = fread(txbuf, 1, blklen, in);
	if (n < blklen)
		Eofseen = 1;
	return n;
#endif
}

#ifdef TXBSIZE
/* Replacement for brain damaged fseek function.  Returns 0==success */
static	int		fooseek(FILE * fptr, long pos, int whence)
{
	long		m, n;

	vfile("fooseek: pos =%lu vpos=%lu Canseek=%d", pos, vpos, Canseek);
	/* Seek offset < current buffer */
	if (pos < (vpos - TXBSIZE + 1024))
	{
		BEofseen = 0;
		if (Canseek > 0)
		{
			vpos = pos & ~TXBMASK;
			if (vpos >= pos)
				vpos -= TXBSIZE;
			if (fseek(fptr, vpos, 0))
				return 1;
		} else if (Canseek == 0)
		{
			if (fseek(fptr, vpos = 0L, 0))
				return 1;
		} else
			return 1;
		while (vpos < pos)
		{
			n = fread(Txb, 1, TXBSIZE, fptr);
			vpos += n;
			vfile("n=%d vpos=%ld", n, vpos);
			if (n < TXBSIZE)
			{
				BEofseen = 1;
				break;
			}
		}
		vfile("vpos=%ld", vpos);
		return 0;
	}
	/* Seek offset > current buffer (Crash Recovery, etc.) */
	if (pos > vpos)
	{
		if (Canseek)
			if (fseek(fptr, vpos = (pos & ~TXBMASK), 0))
				return 1;
		while (vpos <= pos)
		{
			txbuf = Txb + (vpos & TXBMASK);
			m = TXBSIZE - (vpos & TXBMASK);
			vfile("m=%ld vpos=%ld", m, vpos);
			n = fread(txbuf, 1, m, fptr);
			vfile("n=%ld vpos=%ld", n, vpos);
			vpos += n;
			vfile("bo=%d m=%ld vpos=%ld", txbuf - Txb, m, vpos);
			if (n < m)
			{
				BEofseen = 1;
				break;
			}
		}
		return 0;
	}
	/* Seek offset is within current buffer */
	vfile("within buffer: vpos=%ld", vpos);
	return 0;
}
#define fseek fooseek
#endif


/* VARARGS1 */
static	void	vfile( CONST char *form, ... )
{
	va_list		arg;
	char		tmp[BUFSIZ];

	if ( Verbose > 2 )
	{
		va_start( arg, form );
		vsprintf( tmp, form, arg );
		va_end(arg);
		USR_fputs(tmp, stderr );
		USR_fputs("\n",stderr);
	}
}

#ifndef	__TOWNS__
static	void	alrm(void)
{
	longjmp(tohere, -1);
}
#endif


/*
 * readline(timeout) reads character(s) from file descriptor 0 timeout is in
 * tenths of seconds
 */
static	int		readline(int timeout)
{
	register int c;
	static char byt[1];

	if (setjmp(tohere))
	{
		zperr("TIMEOUT");
		return TIMEOUT;
	}
#ifdef	__TOWNS__
	c = timeout;
	if (c < 200 )
		c = 200;
	if (Verbose > 5)
	{
		USR_fprintf(stderr, "Timeout=%4d Calling alarm(%4d) ", timeout, c);
	}
	{
		clock_t	clk;
		clk = H_CLOCK2(0) + c * CLOCKS_PER_SEC / 100;
		do
		{	if ( (c = RS_chk(RsPort)) > 0 )
			{
				byt[0] = RS_getc(RsPort);
				break;
			}
		} while ( clk > H_CLOCK2(clk) );
	}
#else
	c = timeout / 10;
	if (c < 2)
		c = 2;
	if (Verbose > 5)
	{
		USR_fprintf(stderr, "Timeout=%5d Calling alarm(%d) ", timeout, c);
	}
	signal(SIGALRM, alrm);
	alarm(c);
	c = read(0, byt, 1);
	alarm(0);
#endif
	if (Verbose > 5)
	{
		if (c < 1)
			USR_fprintf(stderr, "timeout!!\n" );
		else
			USR_fprintf(stderr, "ret %02X\n", byt[0] & 0377);
	}
	if (c < 1)
		return TIMEOUT;
	return (byt[0] & 0377);
}

static	void	flushmo(void)
{
}


static	void	purgeline(void)
{
#ifndef	__TOWNS__
#ifdef USG
	ioctl(0, TCFLSH, 0);
#else
	lseek(0, 0L, 2);
#endif
#endif
}

/* send cancel string to get the other end to shut up */
static	void	canit(void)
{
	static char canistr[] =
	{
	 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 0
	};

	RS_puts(RsPort,canistr);
}

/* Log an error */
/* VARARGS1 */
#ifdef	__HIGHC__
static	void	zperr(char *form, ... )
{
	va_list		arg;
	char		tmp[BUFSIZ];

	if (Verbose <= 0)
		return;
	USR_fprintf(stderr, "Retry %d: ", errors);
	va_start( arg, form );
	vsprintf( tmp, form, arg );
	va_end(arg);
	USR_fputs(tmp, stderr );
	USR_fputs("\n",stderr);
}
#else
static	void	zperr(char *s, char *p, char *u)
{
	if (Verbose <= 0)
		return;
	USR_fprintf(stderr, "Retry %d: ", errors);
	USR_fprintf(stderr, s, p, u);
	USR_fprintf(stderr, "\n");
}
#endif

/*
 * substr(string, token) searches for token in string s returns pointer to
 * token within string if found, NULL otherwise
 */
static	char	*substr(register char *s, register char *t)
{
	register char *ss, *tt;
	/* search for first char of token */
	for (ss = s; *s; s++)
		if (*s == *t)
			/* compare token with substring */
			for (ss = s, tt = t;;)
			{
				if (*tt == 0)
					return s;
				if (*ss++ != *tt++)
					break;
			}
	return NULL;
}

#include	"szhelp.c"

/* Send send-init information */
static	int		getzrxinit(void)
{
	register	n;
	struct stat f;

	for (n = 10; --n >= 0;)
	{

		switch (zgethdr(Rxhdr, 1))
		{
			case ZCHALLENGE:	/* Echo receiver's challenge numbr */
				stohdr(Rxpos);
				zshhdr(4, ZACK, Txhdr);
				continue;
			case ZCOMMAND:		/* They didn't see out ZRQINIT */
				stohdr(0L);
				zshhdr(4, ZRQINIT, Txhdr);
				continue;
			case ZRINIT:
				Rxflags = 0377 & Rxhdr[ZF0];
				Usevhdrs = Rxhdr[ZF1] & CANVHDR;
				Txfcs32 = (Wantfcs32 && (Rxflags & CANFC32));
				Zctlesc |= Rxflags & TESCCTL;
				Rxbuflen = (0377 & Rxhdr[ZP0]) + ((0377 & Rxhdr[ZP1]) << 8);
				if (!(Rxflags & CANFDX))
					Txwindow = 0;
				vfile("Rxbuflen=%d Tframlen=%d", Rxbuflen, Tframlen);
#ifndef	__TOWNS__
				if (!Fromcu)
					signal(SIGINT, SIG_IGN);
#else
				if (!Fromcu)
				{
					SIGNAL(SIGINT, SIG_IGN);
					CtrlX = FALSE;
				}
#endif
#ifdef MODE2OK
				mode(2);		/* Set cbreak, XON/XOFF, etc. */
				/* set XON/XOFF for sb/sz with ZMODEM or YMODEM-g */
#endif

#ifndef READCHECK
#ifndef USG
				/* Use 1024 byte frames if no sample/interrupt */
				if (Rxbuflen < 32 || Rxbuflen > 1024)
				{
					Rxbuflen = 1024;
					vfile("Rxbuflen=%d", Rxbuflen);
				}
#endif
#endif

				/* Override to force shorter frame length */
				if (Rxbuflen && (Rxbuflen > Tframlen) && (Tframlen >= 32))
					Rxbuflen = Tframlen;
				if (!Rxbuflen && (Tframlen >= 32) && (Tframlen <= 1024))
					Rxbuflen = Tframlen;
				vfile("Rxbuflen=%d", Rxbuflen);

				/* If using a pipe for testing set lower buf len */
#ifdef	__TOWNS__
			//	Rxbuflen = 1024;
#else
				fstat(0, &f);
				if ((f.st_mode & S_IFMT) != S_IFCHR)
				{
					Rxbuflen = 1024;
				}
#endif
				/*
				 * If input is not a regular file, force ACK's to prevent
				 * running beyond the buffer limits
				 */
				if (!Command)
				{
#ifdef	_FSTAT_IGN
					stat(infile, &f);
#else
					fstat(fileno(in), &f);
#endif
					if ((f.st_mode & S_IFMT) != S_IFREG)
					{
						Canseek = -1;
#ifdef TXBSIZE
						Txwindow = TXBSIZE - 1024;
						Txwspac = TXBSIZE / 4;
#else
						return ERROR;
#endif
					}
				}
				/* Set initial subpacket length */
				if (blklen < 1024)
				{				/* Command line override? */
					if (Effbaud > 300)
						blklen = 256;
					if (Effbaud > 1200)
						blklen = 512;
					if (Effbaud > 2400)
						blklen = 1024;
				}
				if (Rxbuflen && blklen > Rxbuflen)
					blklen = Rxbuflen;
				if (blkopt && blklen > blkopt)
					blklen = blkopt;
				vfile("Rxbuflen=%d blklen=%d", Rxbuflen, blklen);
				vfile("Txwindow = %u Txwspac = %d", Txwindow, Txwspac);


				if (Lztrans == ZTRLE && (Rxflags & CANRLE))
					Txfcs32 = 2;
				else
					Lztrans = 0;

				return (sendzsinit());
			case ZCAN:
			case TIMEOUT:
				return ERROR;
			case ZRQINIT:
				if (Rxhdr[ZF0] == ZCOMMAND)
					continue;
			default:
				zshhdr(4, ZNAK, Txhdr);
				continue;
		}
	}
	return ERROR;
}

/* Send send-init information */
static	int		sendzsinit(void)
{
	register	c;

	if (Myattn[0] == '\0' && (!Zctlesc || (Rxflags & TESCCTL)))
		return OK;
	errors = 0;
	for (;;)
	{
		stohdr(0L);
#ifdef ALTCANOFF
		Txhdr[ALTCOFF] = ALTCANOFF;
#endif
		if (Zctlesc)
		{
			Txhdr[ZF0] |= TESCCTL;
			zshhdr(4, ZSINIT, Txhdr);
		} else
			zsbhdr(4, ZSINIT, Txhdr);
		zsdata(Myattn, ZATTNLEN, ZCRCW);
		c = zgethdr(Rxhdr, 1);
		switch (c)
		{
			case ZCAN:
				return ERROR;
			case ZACK:
				return OK;
			default:
				if (++errors > 19)
					return ERROR;
				continue;
		}
	}
}

/* Send file name and related info */
static	int		zsendfile(char *buf, int blen)
{
	register	c;
	register UNSL long crc;
	long		lastcrcrq = -1;
//	char	   *p;

#ifdef	DEBUG
	USR_fprintf(stderr,"zsendfile(buf,buflen = %d)\n", blen);
#endif
	for (errors = 0; ++errors < 11;)
	{
		Txhdr[ZF0] = Lzconv;	/* file conversion request */
		Txhdr[ZF1] = Lzmanag;	/* file management request */
		if (Lskipnocor)
			Txhdr[ZF1] |= ZMSKNOLOC;
		Txhdr[ZF2] = Lztrans;	/* file transport request */
		Txhdr[ZF3] = 0;
		zsbhdr(4, ZFILE, Txhdr);
		zsdata(buf, blen, ZCRCW);
	  again:
		c = zgethdr(Rxhdr, 1);
		switch (c)
		{
			case ZRINIT:
				while ((c = readline(50)) > 0)
				{
					if ( c == ZPAD )
						goto again;
				}
				continue;
			case ZCAN:
			case TIMEOUT:
			case ZABORT:
			case ZFIN:
				sprintf(endmsg, "Got %s on pathname", frametypes[c + FTOFFSET]);
				return ERROR;
			default:
				sprintf(endmsg, "Got %d frame type on pathname", c);
				return ERROR;
			case ERROR:
			case ZNAK:
				continue;
			case ZCRC:
				if (Rxpos != lastcrcrq)
				{
					lastcrcrq = Rxpos;
					crc = 0xFFFFFFFFL;
					if (Canseek >= 0)
					{
						fseek(in, 0L, 0);
						while ( ((c = fgetc(in)) != EOF) && --lastcrcrq )
							crc = UPDC32(c, crc);
						crc = ~crc;
						clearerr(in);	/* Clear possible EOF */
						lastcrcrq = Rxpos;
					}
				}
				stohdr(crc);
				zsbhdr(4, ZCRC, Txhdr);
				goto again;
			case ZFERR:
			case ZSKIP:
				sprintf(endmsg, "File skipped by receiver request");
				FM_fclose(in);
				return c;
			case ZRPOS:
				/*
				 * Suppress zcrcw request otherwise triggered by
				 * lastyunc==bytcnt
				 */
				if (fseek(in, Rxpos, 0))
					return ERROR;
				Lastsync = (long)(bytcnt = Txpos = Lrxpos = Rxpos) - 1l;
				return zsendfdata();
		}
	}
}

/* Send the data in the file */
static	int		zsendfdata(void)
{
	register	c, e, n;
	register	newcnt;
	register long tcount = 0;
	int 		junkcount;		/* Counts garbage chars received by TX */
	static int	tleft = 6;		/* Counter for test mode */

#ifdef	DEBUG
	USR_fprintf(stderr,"zsenfdata \n");
#endif
	junkcount = 0;
	Beenhereb4 = FALSE;
somemore:
	if ( setjmp(intrjmp) )
	{
	  waitack:
		junkcount = 0;
		c = getinsync(0);
	  gotack:
		switch (c)
		{
			default:
			case ZCAN:
				FM_fclose(in);
				return ERROR;
			case ZSKIP:
				FM_fclose(in);
				return c;
			case ZACK:
			case ZRPOS:
				break;
			case ZRINIT:
				FM_fclose(in);	in = NULL;
				return OK;
		}
#ifdef READCHECK
		/*
		 * If the reverse channel can be tested for data, this logic may be
		 * used to detect error packets sent by the receiver, in place of
		 * setjmp/longjmp rdchk(fd) returns non 0 if a character is available
		 */
		while (rdchk(0))
		{
#ifdef EATSIT
			switch (checked)
#else
			switch (readline(1))
#endif
			{
				case CAN:
				case ZPAD:
					c = getinsync(1);
					goto gotack;
				case XOFF:		/* Wait a while for an XON */
				case XOFF | 0200:
					readline(100);
			}
		}
#endif
	}
#ifndef	__TOWNS__
	if (!Fromcu)
		signal(SIGINT, onintr);
#else
	if (!Fromcu)
	{
		CtrlX = TRUE;
		SIGNAL(SIGINT, onintr);
	}
#endif
	newcnt = Rxbuflen;
	Txwcnt = 0;
	stohdr(Txpos);
	zsbhdr(4, ZDATA, Txhdr);

	/*
	 * Special testing mode.  This should force receiver to Attn,ZRPOS many
	 * times.  Each time the signal should be caught, causing the file to be
	 * started over from the beginning.
	 */
	if (Test)
	{
		if (--tleft)
			while (tcount < 20000)
			{
				RS_puts(RsPort,qbf);
				tcount += strlen(qbf);
#ifdef READCHECK
				while (rdchk(0))
				{
#ifdef EATSIT
					switch (checked)
#else
					switch (readline(1))
#endif
					{
						case CAN:
						case ZPAD:
#ifndef	__TOWNS__
#	ifdef TCFLSH
							ioctl(0, TCFLSH, 1);
#	endif
#endif
							goto waitack;
						case XOFF:		/* Wait for XON */
						case XOFF | 0200:
							readline(100);
					}
				}
#endif
			}
#ifndef	__TOWNS__
		signal(SIGINT, SIG_IGN);
#else
		SIGNAL(SIGINT, SIG_IGN);
		CtrlX = FALSE;
#endif
		canit();
#ifdef	__TOWNS__
		{
			clock_t	clk;
			clk = H_CLOCK2(0) + 3 * CLOCKS_PER_SEC;
			while ( clk > H_CLOCK2(clk) )
				;
		}
#else
		sleep(3);
#endif
		purgeline();
		mode(0);	/* restore original tty mode */
		USR_printf("\nsz: Tcount = %ld\n", tcount);
		if (tleft)
		{
			USR_printf("ERROR: Interrupts Not Caught\n");
			EXIT(1);
		}
		EXIT(0);
	}
	do
	{
		n = zfilbuf();
		if (Eofseen)
			e = ZCRCE;
		else if (junkcount > 3)
			e = ZCRCW;
		else if (bytcnt == Lastsync)
			e = ZCRCW;
		else if (Rxbuflen && (newcnt -= n) <= 0)
			e = ZCRCW;
		else if (Txwindow && (Txwcnt += n) >= Txwspac)
		{
			Txwcnt = 0;
			e = ZCRCQ;
		} else
			e = ZCRCG;
		if (Verbose > 1)
		{
			USR_fprintf(stderr, "\r%7ld ZMODEM%s    ",
					Txpos, Crc32t ? " CRC-32" : "");
		}
		zsdata(txbuf, n, e);
		bytcnt = Txpos += n;
		if (e == ZCRCW)
			goto waitack;
#ifdef READCHECK
		/*
		 * If the reverse channel can be tested for data, this logic may be
		 * used to detect error packets sent by the receiver, in place of
		 * setjmp/longjmp rdchk(fd) returns non 0 if a character is available
		 */
		while ( rdchk(0) )
		{
#ifdef EATSIT
			switch (checked)
#else
			switch ( readline(1) )
#endif
			{
				case CAN:
				case ZPAD:
					c = getinsync(1);
					if (c == ZACK)
						break;
#ifndef	__TOWNS__
#	ifdef TCFLSH
					ioctl(0, TCFLSH, 1);
#	endif
#endif
					/* zcrce - dinna wanna starta ping-pong game */
					zsdata(txbuf, 0, ZCRCE);
					goto gotack;
				case XOFF:		/* Wait a while for an XON */
				case XOFF | 0200:
					readline(100);
				default:
					++junkcount;
			}
		}
#endif							/* READCHECK */
		if (Txwindow)
		{
			while ((tcount = (Txpos - Lrxpos)) >= Txwindow)
			{
				vfile("%ld window >= %u", tcount, Txwindow);
				if (e != ZCRCQ)
					zsdata(txbuf, 0, e = ZCRCQ);
				c = getinsync(1);
				if (c != ZACK)
				{
#ifndef	__TOWNS__
#ifdef TCFLSH
					ioctl(0, TCFLSH, 1);
#endif
#endif
					zsdata(txbuf, 0, ZCRCE);
					goto gotack;
				}
			}
			vfile("window = %ld", tcount);
		}
	} while (!Eofseen);
#ifndef	__TOWNS__
	if (!Fromcu)
		signal(SIGINT, SIG_IGN);
#else
	if (!Fromcu)
	{
		SIGNAL(SIGINT, SIG_IGN);
		CtrlX = FALSE;
	}
#endif

	for (;;)
	{
		stohdr(Txpos);
		zsbhdr(4, ZEOF, Txhdr);
		switch (getinsync(0))
		{
			case ZACK:
				continue;
			case ZRPOS:
				goto somemore;
			case ZRINIT:
				FM_fclose(in);	in = NULL;
				return OK;
			case ZSKIP:
				FM_fclose(in);	in = NULL;
				sprintf(endmsg, "File skipped by receiver request");
				return c;
			default:
				sprintf(endmsg, "Got %d trying to send end of file", c);
				FM_fclose(in);	in = NULL;
				return ERROR;
		}
	}
}

/*
 * Respond to receiver's complaint, get back in sync with receiver
 */
static	int		getinsync(int flag)
{
	register	c;

#ifdef	DEBUG
	USR_fprintf(stderr,"getinsync(int %d)\n", flag );
#endif
	for (;;)
	{
		if (Test)
		{
			RS_puts(RsPort,"\r\n\n\n***** Signal Caught *****\r\n");
			Rxpos = 0;
			c = ZRPOS;
		} else
			c = zgethdr(Rxhdr, 0);
		switch (c)
		{
			case ZCAN:
			case ZABORT:
			case ZFIN:
			case TIMEOUT:
				sprintf(endmsg, "Got %s sending data", frametypes[c + FTOFFSET]);
				return ERROR;
			case ZRPOS:
				/* ************************************* */
				/* If sending to a buffered modem, you	*/
				/* might send a break at this point to */
				/* dump the modem's buffer.		 */
				clearerr(in);	/* In case file EOF seen */
				if (fseek(in, Rxpos, 0))
					return ERROR;
				Eofseen = 0;
				bytcnt = Lrxpos = Txpos = Rxpos;
				if (Lastsync == Rxpos)
				{
#ifdef	DEBUG
					if (++Beenhereb4 > 32)
#else
					if (++Beenhereb4 > 12)
#endif
					{
						sprintf(endmsg, "Can't send block");
						return ERROR;
					}
					if (Beenhereb4 > 4)
					{
						if (blklen > 32)
							blklen /= 2;
					}
				}
				Lastsync = Rxpos;
				return c;
			case ZACK:
				Lrxpos = Rxpos;
				if (flag || Txpos == Rxpos)
					return ZACK;
				continue;
			case ZRINIT:
				return c;
			case ZSKIP:
				sprintf(endmsg, "File skipped by receiver request");
				return c;
			case ERROR:
			default:
				zsbhdr(4, ZNAK, Txhdr);
				continue;
		}
	}
}


/* Say "bibi" to the receiver, try to do it cleanly */
static	void	saybibi(void)
{
	for (;;)
	{
		stohdr(0L); 			/* CAF Was zsbhdr - minor change */
		zshhdr(4, ZFIN, Txhdr); /* to make debugging easier */
		switch (zgethdr(Rxhdr, 0))
		{
			case ZFIN:
				sendline('O');
				sendline('O');
				flushmo();
			case ZCAN:
			case TIMEOUT:
				return;
		}
	}
}

/* Local screen character display function */
static	void	bttyout(int c)
{
	if (Verbose)
		USR_fputc(c, stderr);
}

/* Send command and related info */
static	int		zsendcmd(char *buf, int blen)
{
	register	c;
	long		cmdnum;

#ifdef	DEBUG
	vfile("zsendcmd()");
#endif
#ifdef	__HIGHC__
	cmdnum = _getpid();
#else
	cmdnum = getpid();
#endif

	errors = 0;
	for (;;)
	{
		stohdr(cmdnum);
		Txhdr[ZF0] = Cmdack1;
		zsbhdr(4, ZCOMMAND, Txhdr);
		zsdata(buf, blen, ZCRCW);
	  listen:
		Rxtimeout = 100;		/* Ten second wait for resp. */
		Usevhdrs = 0;			/* Allow rx to send fixed len headers */
		c = zgethdr(Rxhdr, 1);

		switch (c)
		{
			case ZRINIT:
				goto listen;	/* CAF 8-21-87 */
			case ERROR:
			case GCOUNT:
			case TIMEOUT:
				if (++errors > Cmdtries)
					return ERROR;
				continue;
			case ZCAN:
			case ZABORT:
			case ZFIN:
			case ZSKIP:
			case ZRPOS:
				return ERROR;
			default:
				if (++errors > 20)
					return ERROR;
				continue;
			case ZCOMPL:
				Exitcode = Rxpos;
				saybibi();
				return OK;
			case ZRQINIT:
				vfile("******** RZ *******");
#ifndef	__TOWNS__
				system("rz");
#endif
				vfile("******** SZ *******");
				goto listen;
		}
	}
}

/*
 * If called as sb use YMODEM protocol
 */
static	void	chkinvok(char *s)
{
	register char *p;

	p = s;
	while (*p == '-')
		s = ++p;
	while (*p)
		if (*p++ == '/')
			s = p;
	if (*s == 'v')
	{
		Verbose = 1;
		++s;
	}
	Progname = s;
	if (s[0] == 's' && s[1] == 'b')
	{
		Nozmodem = TRUE;
		blklen = 1024;
	}
	if (s[0] == 's' && s[1] == 'x')
	{
		Modem2 = TRUE;
	}
}

static	void	countem(int argc, register char **argv)
{
	struct stat f;

	Totalleft = 0;
	Filesleft = 0;
	for ( ; --argc >= 0; ++argv)
	{
		f.st_size = -1;
		if (Verbose > 2)
		{
			USR_fprintf(stderr, "\nCountem: %03d %s ", argc, *argv);
		}
#ifdef	__TOWNS__
		if (_access(*argv, 3) >= 0 && stat(*argv, &f) >= 0)
#else
		if ( _access(*argv, 04) >= 0 && stat(*argv, &f) >= 0)
#endif
		{
#ifndef	__TOWNS__
			register	c;
			c = f.st_mode & S_IFMT;
			if (c != S_IFDIR && c != S_IFBLK)
			{
#endif
				++Filesleft;
				Totalleft += f.st_size;
#ifndef	__TOWNS__
			}
#endif
		}
		if ( Verbose > 2 )
			USR_fprintf(stderr, " %ld", f.st_size);
	}
	if ( Verbose > 2 )
		USR_fprintf(stderr, "\ncountem: Total %d %ld\n",
				Filesleft, Totalleft);
}

static	void	chartest(int m)
{
	register	n;

	mode(m);
	RS_printf(RsPort,"\r\n\nCharacter Transparency Test Mode %d\r\n", m);
	RS_printf(RsPort,"If Pro-YAM/ZCOMM is not displaying ^M hit ALT-V NOW.\r\n");
	RS_printf(RsPort,"Hit Enter.\021");
	readline(500);

	for (n = 0; n < 256; ++n)
	{
		if (!(n % 8))
			RS_puts(RsPort,"\r\n");
		RS_printf(RsPort,"%02x ", n);
		sendline(n);
		flushmo();
		RS_puts(RsPort,"  ");
		if (n == 127)
		{
			RS_puts(RsPort,"Hit Enter.\021");
			readline(500);
			RS_puts(RsPort,"\r\n");
		}
	}
	RS_puts(RsPort,"\021\r\nEnter Characters, echo is in hex.\r\n");
	RS_puts(RsPort,"Hit SPACE or pause 40 seconds for exit.\r\n");

	while (n != TIMEOUT && n != ' ')
	{
		n = readline(400);
		RS_printf(RsPort,"%02x\r\n", n);
	}
	RS_printf(RsPort,"\r\nMode %d character transparency test ends.\r\n", m);
}

/* End of sz.c */
