/****************************************************************************
*	Language 	:	Turbo C 2.0			
*	Logfile		:	sz.c					
*	Project		:	Comms library.		
*	Date 		:	24 Jan 90				
*	Revision 	:	1.1		GT	PC version.							
*	07 Mar 90	:	1.2		GT	Background transfer.				
*	25 Oct 92	:	1.3		GT	Mods for KA9Q.						
*	30 Jan 93	:	1.4		GT	Fix KA9Q background working.	
*	21 Feb 93	:	1.5		GT	Fix reporting.						
*	08 May 93	:	1.6		GT	Fix warnings.						
*	24 Jan 94	:	1.7		GT	_flush -> _rbsb_flush.					
*****************************************************************************
*	Purpose		:	File send driver.	
*****************************************************************************
*				:	This module is based on the equivalent module in the
*				:	31 Aug 87 version of rz/sz.								
*	$Id: sz.c 1.5 94/01/26 13:55:15 ROOT_DOS Exp $
*
*  ATARI Version by David Nash - dnash@chaos.demon.co.uk
*
****************************************************************************/

#include	<io.h>
#include	<fcntl.h>
#include	<stdio.h>
#include	<stdlib.h>
#include	<signal.h>
#include	<setjmp.h>
#include	<ctype.h>
#include	<stdarg.h>
#include	<time.h>
#include	<process.h>
#include	<string.h>
#include	<io.h>
#include	<sys/types.h>
#include	<sys/stat.h>
#include	<dos.h>
#ifdef ATARI
#include <ext.h>
#endif
#include "config.h"
#include	"socket.h"
#include	"proc.h"

/* kludge for krappy Microsoft */

#ifndef	S_IFBLK
#define	S_IFBLK		0x3000
#endif

#include	"zmodem.h"
#include	"sz.h"
#include	"rbsb.h"			/* most of the system dependent stuff here	*/
#include	"zm.h"
#ifdef	DEBUGZ
#include	"tty.h"
#endif


#define HOWMANY		2048
#define	NO_DCD		1

/****************************************************************************
*	Local prototypes.														*
****************************************************************************/

#if	0
static void bibi (int n);
#endif
static void chkinvok (char protocol);
static int readline (int n);
static void purgeline (void);
#if	0
static void onintr (void);
#endif
static int wcsend (int argc, char **argp);
static int wcs (char *oname);
static int wctxpn (char *name);
static int getnak (void);
static int wctx (long flen);
static int wcputsec (char *buf, int sectnum, int cseclen);
static int filbuf (char *buf, int count);
static int zfilbuf (char *buf, int count);
static int readock (int timeout, int count);
static int getzrxinit (void);
static int sendzsinit (void);
static int zsendfile (char *buf, int blen);
static int zsendfdata (void);
static int getinsync (int flag);
static void saybibi (void);
static int zsendcmd (char *buf, int blen);


/****************************************************************************
*	Global data.															*
****************************************************************************/

int _Zmodem = 0;				/* ZMODEM protocol requested				*/
unsigned _Baud_z = 19200;
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			*/
int _errors;
int _sending;					/* TRUE - sending a file.					*/
int _zperr_handle;				/* -> log file								*/

/*
 * 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.
 */
 
static char Myattn[] = { 0 };

static int in;

int _Lastrx;								/* was char						*/
int _Crcflg;								/* was char						*/
int _Wcsmask = 0377;

/* Command line option flags */

int _Verbose = 0;
static int Modem2 = 0;			/* XMODEM Protocol - don't send pathnames	*/
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		*/
int _firstsec;
static int errcnt = 0;			/* number of files unreadable				*/
static int blklen = SECSIZ;		/* length of transmitted records			*/
static int Optiong;				/* Let it rip no wait for sector ACK's		*/
static int Noeofseen;
static int Totsecs;				/* total number of sectors this file		*/
static char txbuf[KSIZE];
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		*/
char _Lzmanag;					/* Local ZMODEM file management request		*/
static int Lskipnocor;
static char Lztrans;
char _zconv;					/* ZMODEM file conversion request			*/
char _zmanag;					/* ZMODEM file management request			*/
char _ztrans;					/* ZMODEM file transport request			*/
static int Command;				/* Send a command, then exit.				*/
static char Cmdstr[256];		/* Command string							*/
static int Cmdtries = 11;
static int Cmdack1;				/* Rx ACKs command, then do it				*/
static int Exitcode;
static int Testattn;			/* Force receiver to send Attn, etc with qbf. */
static char *qbf = "The quick brown fox jumped over the lazy dog's back "
				   "1234567890\r\n";
static long Lastread;			/* Beginning offset of last buffer read		*/
static int Lastn;				/* Count of last buffer read or -1			*/
static int Dontread;			/* Don't read the buffer, it's still there	*/
static long Lastsync;			/* Last offset to which we got a ZRPOS		*/
static int Beenhereb4;			/* How many times we've been ZRPOS'd same
								   place									*/

jmp_buf _tohere;					/* For user abort.							*/
static jmp_buf intrjmp;			/* For the interrupt on RX CAN.				*/
jmp_buf _nocarrier;				/* For carrier dropped.						*/

int _Zctlesc;					/* Encode control characters				*/
int _Nozmodem = 0;				/* If invoked as "sb"						*/
int _Zrwindow = 1400;			/* RX window size (controls garbage count)	*/
void (*_do_report) (int type, void *data) = _zperr;
								/* -> progress report fn					*/

extern int Lleft;				/* number of chars in read buffer			*/
extern int Readnum;				/* how many bytes to read					*/


#if	0
/****************************************************************************
*	bibi																	*
*	Called by signal interrupt or terminate to clean things up.				*
****************************************************************************/

static void bibi (int n)
	{
	n = n;
	_canit ();
	_zperr_ ("Interrupted\n");
	longjmp (_tohere, -1);
	}	/* static void bibi (int n) */


/****************************************************************************
*	onintr																	*
*	Called when Zmodem gets an interrupt (^X).								*
****************************************************************************/

static void onintr (void)
	{
#if	0
	signal (SIGINT, SIG_IGN);
#endif
	longjmp (intrjmp, -1);
	}	/* static void onintr (void) */
#endif


/****************************************************************************
*	_no_carrier																*
*	Tests to see if CD still present, exits if not.							*
****************************************************************************/

void _no_carrier (void)
	{
#if	!defined (NO_DCD)
	if ((modemstat () & 0x80) == 0)
		{
		_zperr_ ("No carrier\n");
		longjmp (_nocarrier, -1);
		}

#endif
	}	/* void _no_carrier (void) */
	

/****************************************************************************
*	_sendline and _xsendline												*
*	Send a character to remote.												*
****************************************************************************/

void _sendline (int c)
	{
	if (_sending)
		c &= _Wcsmask;

	_xsendline (c);
	}	/* void _sendline (int c) */


void _xsendline (int c)
	{
#ifdef	DEBUGZ
	if (_Verbose > 6)
		_vfile ("_xsendline: %x\n", c);
#endif

	while (_send ((unsigned char *) &c, 1) != 1)
		_no_carrier ();
		
	}	/* void _xsendline (int c) */
	

/****************************************************************************
*	_flushmo																*
*	Flush the ouput channel.												*
****************************************************************************/

void _flushmo (void)
	{
	_rbsb_flush ();
	}	/* void _flushmo (void) */


/****************************************************************************
*	_sendfile																*
*	Send file(s).  Returns 0 if successful.									*
****************************************************************************/

int _sendfile (int s, char protocol, char *options, char *filenames[],
			   void (*reporter) (int type, void *data))
	{
	char *cp;
	int npats;
	int agcnt;
	char **agcv;
	char **patts;
	int i;									/* loop counter					*/
#if	0
	void (*sigint_sav) (int);
	void (*sigterm_sav) (int);
#endif
	
	/* Initialise global variables */

	_z_socket = s;
	Lleft = 0;
	Readnum = HOWMANY;
	Ascii = 0;
	blklen = SECSIZ;
	blkopt = 0;
	Cmdtries = 11;
	Command = FALSE;
	_do_report = _zperr;
	Dottoslash = 0;
	errcnt = 0;
	Exitcode = 0;
	Filcnt = 0;
	Fullname = 0;
	Lfseen = 0;
	Modem2 = 0;
	_Nozmodem = 0;
	_Quiet = 0;
	Rxbuflen = 16384;
	Rxflags = 0;
	_sending = TRUE;
	Tframlen = 0;
	Unlinkafter = 0;
	Wantfcs32 = TRUE;
	_Wcsmask = 0377;
	_Verbose = 0;
	_Zmodem = 0;
	_Zrwindow = 1400;

	/* See if null padding required */
	
	if (((cp = getenv ("ZNULLS")) != NULL) && *cp)
		_Znulls = atoi (cp);
	
	chkinvok (protocol);					/* select protocol				*/
	_Rxtimeout = 600;
	npats = 0;

	/* Parse options. */
	
	cp = options;
	while (*cp != '\0')
		{
		switch (*cp++)
			{
			case '+':						/* append file					*/
				_Lzmanag = ZMAPND;
				break;

			case '7':						/* strip top bit				*/
				_Wcsmask = 0177;
				break;

			case 'a':						/* NL -> CRNL conversion		*/
				Lzconv = ZCNL;
				Ascii = TRUE;
				break;

			case 'b':						/* binary file					*/
				Lzconv = ZCBIN;
				break;

			case 'i':						/* command, no wait				*/
				Cmdack1 = ZCACK1;

				/* **** FALL THROUGH TO **** */

			case 'c':						/* command, wait				*/
				if (*cp != '#')				/* command delimiter?			*/
					return (ERROR);			/* invalid parameter			*/

				Command = TRUE;
				for (i = 0; i < sizeof (Cmdstr); i++)
					{
					if (*++cp == '#')
						break;				/* end of command				*/
						
					Cmdstr[i] = *cp;
					}

				Cmdstr[i] = '\0';
				cp++;
				break;
				
			case 'd':						/* . -> /						*/
				++Dottoslash;
				
				/* **** FALL THROUGH TO **** */

			case 'f':						/* full pathnames				*/
				Fullname = TRUE;
				break;
			
			case 'e':						/* escape ctl chars				*/
				_Zctlesc = 1;
				break;

			case 'k':						/* YMODEM 1k					*/
				blklen = KSIZE;
				break;

			case 'L':						/* limit subpacket length		*/
				sscanf (cp, "%d", &blkopt);
				if (blkopt < 24 || blkopt > 1024)
					return (ERROR);			/* bad length					*/

				cp = _stbnb (cp);			/* skip whitespace				*/
				break;
				
			case 'l':						/* limit frame length			*/
				sscanf (cp, "%d", &Tframlen);
				if (Tframlen < 32 || Tframlen > 1024)
					return (ERROR);

				cp = _stbnb (cp);
				break;
				
			case 'N':						/* source newer or longer		*/
				_Lzmanag = ZMNEWL;
				break;
				
			case 'n':						/* source newer					*/
				_Lzmanag = ZMNEW;
				break;
				
			case 'o':						/* 16 bit CRC					*/
				Wantfcs32 = FALSE;
				break;

			case 'p':						/* no target overwrite			*/
				_Lzmanag = ZMPROT;
				break;

			case 'r':						/* resume file transfer			*/
				Lzconv = ZCRESUM;
				break;

			case 'q':						/* quiet						*/
				_Quiet = TRUE;
				_Verbose = 0;
				break;

			case 't':						/* change timeout				*/
				sscanf (cp, "%d", &_Rxtimeout);
				if (_Rxtimeout < 10 || _Rxtimeout > 1000)
					return (ERROR);

				cp = _stbnb (cp);
				break;

			case 'T':						/* test attention mode			*/
				Testattn = TRUE;
				break;

			case 'u':						/* erase after send				*/
				++Unlinkafter;
				break;
				
			case 'v':						/* debugging info				*/
				++_Verbose;
				break;
				
			case 'w':						/* window size					*/
				sscanf (cp, "%d", &Txwindow);
				if (Txwindow < 256)
					Txwindow = 256;
					
				Txwindow = (Txwindow / 64) * 64;
				Txwspac = Txwindow / 4;
				if (blkopt > Txwspac || (!blkopt && Txwspac < 1024))
					blkopt = Txwspac;
					
				cp = _stbnb (cp);
				break;
				
			case 'X':						/* use XMODEM					*/
				++Modem2;
				break;
				
			case 'Y':						/* overwrite target only if
											   present						*/
				Lskipnocor = TRUE;

				/* **** FALL THROUGH TO **** */

			case 'y':						/* overwrite target always		*/
				_Lzmanag = ZMCLOB;
				break;

			default:
				return (ERROR);
			}	/* switch (*cp++) */

		}	/* while (*cp != '\0') */

	/* parse filenames */

	patts = filenames;
	while (**patts != '\0')
		{
		npats++;							/* bump filename count			*/
		patts++;							/* try next string				*/
		}

	patts = filenames;
	if (npats < 1 && !Command) 
		return (ERROR);						/* no filenames					*/

	/* Get reporter function */

	if (reporter != NULL)
		_do_report = reporter;				/* user supplied fn				*/

#if	0
	if (_Verbose)
		{
		if ((_zperr_handle = open (LOGFILE,
								   O_CREAT | O_APPEND | O_WRONLY | O_TEXT,
								   S_IFREG | S_IWRITE)) == -1)
			{
			char buff[80];
			
			sprintf (buff, "Can't open log file %s: %s\n",
					 LOGFILE, strerror (errno));
			(*_do_report) (2, buff);
			(*_do_report) (3, &Filcnt);		/* report no of files			*/
		
			return (ERROR);
			}

		_do_report = _zperr;				/* use default reporter			*/
		}
#endif
	
	if (!_Quiet)
		{
		if (_Verbose == 0)
			_Verbose = 2;
			
		}

#if	0
	if ((sigint_sav = signal (SIGINT, bibi)) == SIG_IGN)
		signal (SIGINT, SIG_IGN);
	else
		signal (SIGINT, bibi);

#endif

#if	0
	sigint_sav = signal (SIGINT, bibi);
	sigterm_sav = signal (SIGTERM, bibi);
#endif
		
	/* Organise escape routes. */

	if (setjmp (_tohere) != 0)
		{
		(*_do_report) (3, &Filcnt);			/* report no of files			*/
#if	0
		signal (SIGINT, sigint_sav);
		signal (SIGTERM, sigterm_sav);
		(void) close (_zperr_handle);
#endif
		return (-2);						/* user abort					*/
		}

	if (setjmp (_nocarrier) != 0)
		{
		(*_do_report) (3, &Filcnt);			/* report no of files			*/
#if	0
		signal (SIGINT, sigint_sav);
		signal (SIGTERM, sigterm_sav);
		(void) close (_zperr_handle);
#endif
		return (-3);						/* lost carrier					*/
		}

	if (!Modem2)
		{
#if	0
		if (!_Nozmodem)
			{
			printf ("rz\r");
			}
#endif

		if (!Command && !_Quiet && _Verbose != 1)
			{
			_say ("%d file%s requested:\r\n",
					 npats, npats > 1 ? "s" : "");
			for (agcnt = npats, agcv = patts; --agcnt >= 0;)
				{
				_say ("%s ", *agcv++);
				}
				
			_say ("\r\n");
			_say ("\r\n\bSending in Batch Mode\r\n");
			}
			
		if (!_Nozmodem)
			{
			_stohdr (0L);
			if (Command)
				_Txhdr[ZF0] = ZCOMMAND;

			_zshhdr (ZRQINIT, _Txhdr);
			}

		}	/* if (!Modem2) */
		
	if (Command)
		{
		if (getzrxinit ())
			{
			Exitcode = -2;
			_canit ();
			}
		else if (zsendcmd (Cmdstr, 1 + strlen (Cmdstr)))
			{
			Exitcode = -2;
			_canit ();
			}
			
		}
	else if (wcsend (npats, patts) == ERROR)
		{
		Exitcode = -2;
		_canit ();
		}
		
	if (!_Quiet)
		_say ("\n");

	(*_do_report) (3, &Filcnt);			/* report no of files			*/
#if	0
	signal (SIGINT, sigint_sav);
	signal (SIGTERM, sigterm_sav);
	(void) close (_zperr_handle);
#endif
	return (Exitcode);
	}	/* int _sendfile (int s, char protocol, char *options,
						  char *filenames[],
						  void (*reporter) (int type, void *data)) */


/****************************************************************************
*	_stbnb																	*
*	Skip to blank then skip to nonblank.									*
****************************************************************************/

char *_stbnb (char *cp)
	{
	/* skip to blank */

	while (!isspace (*cp))
		cp++;
		
	/* skip to nonblank */

	while (isspace (*cp))
		cp++;

	return (cp);
	}	/* char *_stbnb (char *cp) */


/****************************************************************************
*	wcsend																	*
*	Send <argc> strings (<argp>) to remote.									*
****************************************************************************/

static int wcsend (int argc, char **argp)
	{
	int n;

	_Crcflg = FALSE;
	_firstsec = TRUE;
	bytcnt = -1;
	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	0
		if (1)
#endif
			{
			Command = TRUE;
			strcpy (Cmdstr, "echo Can't open any requested files");
			if (getnak ())
				{
				Exitcode = -2;
				_canit ();
				}
				
			if (!_Zmodem)
				_canit ();
			else if (zsendcmd (Cmdstr, 1 + strlen (Cmdstr)))
				{
				Exitcode = -2;
				_canit ();
				}
				
			Exitcode = -2;
			return OK;
			}
			
#if	0									/* unreachable						*/
		_canit ();
		_say ("\r\nCan't open any requested files.\r\n");
		return ERROR;
#endif
		}	/* if (Filcnt == 0) */
		
	if (_Zmodem)
		saybibi ();
	else if (!Modem2)
		wctxpn ("");
		
	return OK;
	}	/* static int wcsend (int argc, char **argp) */


/****************************************************************************
*	wcs																		*
*	Send file <oname> to remote.											*
****************************************************************************/

static int wcs (char *oname)
	{
	int c;
	struct stat f;
	char name[PATHLEN];

	strcpy (name, oname);
	if ((in = open (oname, O_RDONLY | O_BINARY, S_IREAD)) == -1)
		{
		if (_Verbose > 2)
			{
			_say ("Couldn't open %s\r\n", oname);
			}
			
		++errcnt;
		return OK;	/* pass over it, there may be others */
		}
		
	++Noeofseen;
	Lastread = 0;
	Lastn = -1;
	Dontread = FALSE;
	
	/* Check for directory or block special files */
	
	fstat (in, &f);
	c = f.st_mode & S_IFMT;
	if (c == S_IFDIR || c == S_IFBLK)
		{
		close (in);
		return OK;
		}

	++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;
	}	/* static int wcs (char *oname) */
	

/****************************************************************************
*	wctxpn																	*
*	Generate and transmit pathname block consisting of pathname (null 		*
*	terminated), file length, mode time and file mode in octal as provided 	*
*	by the fstat call.														*
*	N.B.: modifies the passed name, may extend it!							*
****************************************************************************/

static int wctxpn (char *name)
	{
	char *p, *q;
	char name2[PATHLEN];
	struct stat f;

	(*_do_report) (0, name);
	if (Modem2)
		{
		if (*name && (fstat (in, &f) != -1))
			{
			_say ("Sending %s, %ld blocks: ", name, f.st_size >> 7);
			}

		_say ("Give your local XMODEM receive command now.\r\n");
		return OK;
		}

	_vfile ("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 					*/
			}
			
		}	/* if (Dottoslash) */

	for (p = name, q = txbuf; *p;)
		if ((*q++ = *p++) == '\\' && !Fullname)
			q = txbuf;
			
	*q++ = 0;
	p = q;
	while (q < (txbuf + KSIZE))
		*q++ = 0;

	if (!Ascii && *name && fstat (in, &f) != -1)
		sprintf (p, "%lu %lo %o", f.st_size, f.st_mtime, f.st_mode);
		
	/* force 1k blocks if name won't fit in 128 byte block */

	if (txbuf[125])
		blklen = KSIZE;
	else
		{
		/* A little goodie for IMP/KMD */
		
		if (_Zmodem)
			blklen = SECSIZ;
			
		txbuf[127] = (char) ((f.st_size + 127) >> 7);
		txbuf[126] = (char) ((f.st_size + 127) >> 15);
		}
		
	if (_Zmodem)
		return zsendfile (txbuf, (int) (1 + strlen (p) + (p - txbuf)));
		
	if (wcputsec (txbuf, 0, SECSIZ) == ERROR)
		return ERROR;
		
	return OK;
	}	/* static int wctxpn (char *name) */


/****************************************************************************
*	getnak																	*
*	Returns FALSE if NAK or ZPAD received.									*
****************************************************************************/

static int getnak (void)
	{
	int firstch;

	_Lastrx = 0;
	for (;;)
		{
		switch (firstch = readock (800, 1))
			{
			case ZPAD:
				if (getzrxinit ())
					return ERROR;

				Ascii = 0;
				return FALSE;
				
			case ZTIMEOUT:
				_zperr_ ("Timeout on pathname");
				return TRUE;
				
			case WANTG:
				Optiong = TRUE;
				blklen=KSIZE;
				
			case WANTCRC:
				_Crcflg = TRUE;
				
			case NAK:
				return FALSE;

			case CAN:
				if ((firstch = readock (20, 1)) == CAN && _Lastrx == CAN)
					return TRUE;
					
			default:
				break;
			}	/* switch (firstch = readock (800, 1)) */
			
		_Lastrx = firstch;
		}	/* for (;;) */
		
	}	/* static int getnak (void) */
	

/****************************************************************************
*	wctx																	*
*	Send <flen> bytes (in blocks) using XMODEM or YMODEM.					*
****************************************************************************/

static int wctx (long flen)
	{
	int thisblklen;
	int sectnum, attempts, firstch;
	long charssent;

	charssent = 0;
	_firstsec = TRUE;
	thisblklen = blklen;
	_vfile ("wctx:file length = %ld", flen);

	while ((firstch = readock (_Rxtimeout, 2)) != NAK &&
		   firstch != WANTCRC &&
		   firstch != WANTG &&
		   firstch != ZTIMEOUT &&
		   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;
		}

	close (in);
	attempts = 0;
	do
		{
		purgeline ();
		_sendline (EOT);
		++attempts;
		firstch = (readock (_Rxtimeout, 1));
		} while (firstch != ACK && attempts < RETRYMAX);
				 
	if (attempts == RETRYMAX)
		{
		_zperr_ ("No ACK on EOT");
		return ERROR;
		}
	else
		return OK;

	}	/* static int wctx (long flen) */


/****************************************************************************
*	wcputsec																*
*	Send block <sectnum> of length <cseclen> using XMODEM or YMODEM.		*
****************************************************************************/

static int wcputsec (char *buf, int sectnum, int cseclen)
	{
	int checksum, wcj;
	char *cp;
	unsigned oldcrc;
	int firstch;
	int attempts;

	firstch = 0;						/* part of logic to detect CAN CAN	*/

	_vfile ("Sector %3d %2dk\n", Totsecs, Totsecs / 8 );
	if (_Verbose > 1)
		_say ("\rSector %3d %2dk ", Totsecs, Totsecs / 8 );

	for (attempts = 0; attempts <= RETRYMAX; attempts++)
		{
		_Lastrx = firstch;
		_sendline (cseclen == KSIZE ? 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);

		if (Optiong)
			{
			_firstsec = FALSE;
			return OK;
			}

		firstch = readock (_Rxtimeout, (Noeofseen && sectnum) ? 2 : 1);

gotnak:
		switch (firstch)
			{
			case CAN:
				if (_Lastrx == CAN)
					{
cancan:
					_zperr_ ("Cancelled");
					return ERROR;
					}

				break;

			case ZTIMEOUT:
				_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;
			}	/* switch (firstch) */
			
		for (;;)
			{
			_Lastrx = firstch;
			if ((firstch = readock (_Rxtimeout, 2)) == ZTIMEOUT)
				break;

			if (firstch == NAK || firstch == WANTCRC)
				goto gotnak;

			if (firstch == CAN && _Lastrx == CAN)
				goto cancan;

			}

		}	/* for (attempts = 0; attempts <= RETRYMAX; attempts++) */
		
	_zperr_ ("Retry Count Exceeded");
	return ERROR;
	}	/* static int wcputsec (char *buf, int sectnum, int cseclen) */
	

/****************************************************************************
*	filbuf																	*
*	Fill <buf> with <count> chars padding with ^Z for CPM.					*
****************************************************************************/

static int filbuf (char *buf, int count)
	{
	int c, m;
	char locbuf[BUFSIZ];				/* local read buffer				*/
	char *q;							/* -> read buffer					*/
	int rcount;							/* no of chars read					*/
	
	if (!Ascii)
		{
		m = read (in, buf, count);
		if (m <= 0)
			return 0;
			
		while (m < count)
			buf[m++] = 032;
			
		return count;
		}
		
	m = count;
	if (Lfseen)
		{
		*buf++ = 012;
		--m;
		Lfseen = 0;
		}
		
#if	0
	while ((c = getc (in)) != EOF)
#endif
	while ((rcount = read (in, locbuf, sizeof (locbuf))) != -1)
		{
		q = locbuf;
		while (rcount-- != 0)
			{
			c = *q++;
			if (c == 012)
				{
				*buf++ = 015;
				if (--m == 0)
					{
					Lfseen = TRUE;
					break;
					}

				}

			*buf++ = (char) c;
			if (--m == 0)
				break;

			}	/* while (rcount-- != 0) */

		if (m == 0)
			break;

		}	/* while ((rcount = read (in, locbuf, sizeof (locbuf)) != -1) */

	if (m == count)
		return 0;
	else
		while (--m >= 0)
			*buf++ = CPMEOF;

	return count;
	}	/* static int filbuf (char *buf, int count) */
	

/****************************************************************************
*	zfilbuf																	*
*	Fill <buf> with <count> chars.											*
****************************************************************************/

static int zfilbuf (char *buf, int count)
	{
#ifndef ATARI		
	if (eof (in))
		return (0);
#endif
		
	return (read (in, buf, count));
#if	0
	int c, m;

	m = count;
	while ((c = getc (in)) != EOF)
		{
		*buf++ = c;
		if (--m == 0)
			break;

		}

	return (count - m);
#endif
	}	/* static int zfilbuf (char *buf, int count) */
	

/****************************************************************************
*	_vfile																	*
*	Print debugging message.												*
****************************************************************************/

void _vfile (char *f, ...)
	{
	va_list parms;							/* -> variable arguments		*/
	char buf[128];							/* for text						*/
	
	va_start (parms, f);
	if (_Verbose > 2)
		{
		vsprintf (buf, f, parms);
		strcat (buf, "\n");
		(*_do_report) (2, buf);				/* call reporter function		*/
		}

	va_end (parms);
	}	/* void _vfile (char *f, ...) */


/****************************************************************************
*	_say																	*
*	Report something unconditionally.										*
****************************************************************************/

void _say (char *f, ...)
	{
	va_list parms;							/* -> variable arguments		*/
	char buf[128];							/* for text						*/
	
	va_start (parms, f);
	vsprintf (buf, f, parms);
	(*_do_report) (2, buf);					/* call reporter function		*/
	va_end (parms);
	}	/* void say (char *f, ...) */


/****************************************************************************
*	readock																	*
*	Reads character(s) from receive channel.  It attempts to read count 	*
*	characters (1 <= <count> <= 3).  If it gets more than one, it is an 	*
*	error unless all are CAN (otherwise, only normal response is ACK, CAN	*
*	or C).  Only looks for one if Optiong, which signifies cbreak, not raw 	*
*	input.  <timeout> is in tenths of seconds.								*
****************************************************************************/

static int readock (int timeout, int count)
	{
	int c, t;
	static unsigned char byt[5];
	time_t start;							/* start time for timeout		*/
	
	if (Optiong)
		count = 1;							/* Special hack for cbreak 		*/

	t = timeout / 10;
	if (t < 2)
		t = 2;

	time (&start);
	if (_Verbose > 5)
		{
		_say ("Timeout=%d Calling alarm (%d) ", timeout, t);
		byt[1] = 0;
		}

	_no_carrier ();
	while (1)
		{
		c = _receive (byt, count);
		if (c != 0)
			break;							/* got something				*/

		if (time (NULL) >= start + t)
			{
			_zperr_ ("TIMEOUT");
			return ZTIMEOUT;
			}

		}
		
	if (_Verbose > 5)
		_say ("ret cnt=%d %x %x\n", c, byt[0], byt[1]);
		
	if (c < 1)
		return ZTIMEOUT;

	if (c == 1)
		return (byt[0] & 0377);
	else
		while (c)
			if (byt[--c] != CAN)
				return ERROR;
				
	return CAN;
	}	/* static int readock (int timeout, int count) */

	
/****************************************************************************
*	readline																*
*	Receive one control character with timeout.								*
****************************************************************************/

static int readline (int n)
	{
	return (readock (n, 1));
	}	/* static int readline (int n) */
	

/****************************************************************************
*	purgeline																*
*	Flush input buffer.														*
****************************************************************************/

static void purgeline (void)
	{
	unsigned char c;

	while (_rdchk () > 0)
		_receive (&c, 1);
		
#if	0
	while (_receive (&c, 1) > 0)
		;
#endif
		
	}	/* static void purgeline (void) */


/****************************************************************************
*	_canit 																	*
*	Send cancel string to get the other end to shut up.						*
****************************************************************************/

void _canit (void)
	{
	int result;
	int count = 0;
#ifdef	DEBUGZ
	char buf[128];
#endif

	static unsigned char canistr[] = { 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
									   8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 0 };

	_flushmo ();
	while (count != strlen ((char *) canistr))
		{
		while ((result = _send (canistr + count,
								strlen ((char *) canistr) - count)) == 0)
			;

		count += result;
#ifdef	DEBUGZ
		sprintf (buf, "_canit: count = %d\r\n", count);
		_tout (buf);
#endif
		}

	_flushmo ();
	}	/* void _canit (void) */


/****************************************************************************
*	_zperr_																	*
*	Log an error.															*
****************************************************************************/

void _zperr_ (char *s, ...)
	{
	va_list parms;
	char buf0[128];							/* for report string			*/
	char buf1[128];
	char buf2[128];
	
	if (_Verbose <= 0)
		return;

	va_start (parms, s);
	sprintf (buf1, "Retry %d: ", _errors);
	vsprintf (buf2, s, parms);
	sprintf (buf0, "%s%s\n", buf1, buf2);
	(*_do_report) (2, buf0);				/* call reporter function		*/
	va_end (parms);
	}	/* void _zperr_ (char *s, ...) */


/****************************************************************************
*	_zperr																	*
*	Default progress report function.										*
****************************************************************************/

void _zperr (int type, void *data)
	{
	switch (type)
		{
		case 0:								/* filename						*/
			tprintf ("FILE: %s\n", data);
			break;

		case 1:								/* bytes transferred			*/
			tprintf ("\r%7ld ", *((long *) data));
			break;

		case 2:								/* other text					*/
			tprintf ("%s", data);
			break;

		case 3:								/* end of transfer				*/
			tprintf ("%5d files transferred\n", *((int *) data));
			break;
			
		default:
			return;
		}	/* switch (type) */

	}	/* void _zperr (int type, void *data) */
	

/****************************************************************************
*	_substr																	*
*	Searches for <token> in string <s>.  Returns pointer to token within 	*
*	string if found, NULL otherwise.										*
****************************************************************************/

char *_substr (char *s, char *t)
	{
	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;
					
				}

			}	/* if (*s == *t) */

		}	/* for (ss = s; *s; s++) */
		
	return NULL;
	}	/* char *_substr (char *s, char *t) */


/****************************************************************************
*	getzrxinit																*
*	Get the receiver's init parameters.										*
****************************************************************************/

static int getzrxinit (void)
	{
	int n;
	struct stat f;

	for (n = 10; --n >= 0;)
		{
		switch (_zgethdr (_Rxhdr, 1))
			{
			case ZCHALLENGE:	 /* Echo receiver's challenge numbr */
				_stohdr (_Rxpos);
				_zshhdr (ZACK, _Txhdr);
				continue;
				
			case ZCOMMAND:			 /* They didn't see out ZRQINIT */
				_stohdr (0L);
				_zshhdr (ZRQINIT, _Txhdr);
				continue;
				
			case ZRINIT:
				Rxflags = 0377 & _Rxhdr[ZF0];
				_Txfcs32 = (Wantfcs32 && (Rxflags & CANFC32));
				_Zctlesc |= Rxflags & TESCCTL;
				Rxbuflen = (0377 & _Rxhdr[ZP0]) + ((0377 & _Rxhdr[ZP1]) << 8);
				_vfile ("Rxbuflen=%d Tframlen=%d", Rxbuflen, Tframlen);
#if	0
				signal (SIGINT, SIG_IGN);
#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 input is not a regular file, force ACK's each 1024
				 *  (A smarter strategey could be used here ...)
				 */

				fstat (in, &f);
				if (((f.st_mode & S_IFMT) != S_IFREG) &&
					(Rxbuflen == 0 || Rxbuflen > 1024))
					Rxbuflen = 1024;

				_vfile ("Rxbuflen=%d", Rxbuflen);

				return (sendzsinit ());

			case ZCAN:
			case ZTIMEOUT:
				return ERROR;

			case ZRQINIT:
				if (_Rxhdr[ZF0] == ZCOMMAND)
					continue;

			default:
				_zshhdr (ZNAK, _Txhdr);
				continue;
			}	/* switch (_zgethdr (_Rxhdr, 1)) */
			
		}	/* for (n = 10; --n >= 0;) */
		
	return ERROR;
	}	/* static int getzrxinit (void) */
	

/****************************************************************************
*	sendzsinit																*
*	Send send-init information.												*
****************************************************************************/

static int sendzsinit (void)
	{
	int c;

	if (Myattn[0] == '\0' && (!_Zctlesc || (Rxflags & TESCCTL)))
		return OK;
		
	_errors = 0;
	for (;;)
		{
		_stohdr (0L);
		if (_Zctlesc)
			{
			_Txhdr[ZF0] |= TESCCTL;
			_zshhdr (ZSINIT, _Txhdr);
			}
		else
			_zsbhdr (ZSINIT, _Txhdr);
			
		_zsdata (Myattn, 1 + strlen (Myattn), ZCRCW);
		c = _zgethdr (_Rxhdr, 1);
		switch (c)
			{
			case ZCAN:
				return ERROR;

			case ZACK:
				return OK;

			default:
				if (++_errors > 19)
					return ERROR;

				continue;
			}	/* switch (c) */
			
		}	/* for (;;) */
		
	}	/* static int sendzsinit (void) */
	

/****************************************************************************
*	zsendfile																*
*	Send file name and related info.										*
****************************************************************************/

static int zsendfile (char *buf, int blen)
	{
	int c;

	for (;;)
		{
		_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 (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;
						}
						
				/* **** FALL THRU TO **** */

			default:
				continue;

			case ZCAN:
			case ZTIMEOUT:
			case ZABORT:
			case ZFIN:
				return ERROR;

			case ZSKIP:
				close (in);
				return c;
				
			case ZRPOS:
				/*
				 * Suppress zcrcw request otherwise triggered by
				 * lastyunc==bytcnt
				 */

				Lastsync = (bytcnt = _Txpos = _Rxpos) - 1;
				lseek (in, _Rxpos, 0);
				Dontread = FALSE;
				return zsendfdata ();
			}	/* switch (c) */
			
		}	/* for (;;) */
		
	}	/* static int zsendfile (char *buf, int blen) */
	

/****************************************************************************
*	zsendfdata																*
*	Send the data in the file.												*
****************************************************************************/

static int zsendfdata (void)
	{
	int c, e, n;
	int newcnt;
	long tcount = 0;
	int junkcount;					/* Counts garbage chars received by TX	*/
	static int tleft = 6;			/* Counter for test mode				*/
	int sent_zm = FALSE;			/* TRUE - sent ZMODEM string			*/
	if (_Baud_z > 300)
		blklen = 256;
		
	if (_Baud_z > 1200)
		blklen = 512;
		
	if (_Baud_z > 2400)
		blklen = KSIZE;
		
	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);
	Lrxpos = 0;
	junkcount = 0;
	Beenhereb4 = FALSE;
	
somemore:
	if (setjmp (intrjmp))
		{
waitack:
		junkcount = 0;
		c = getinsync (0);

gotack:
		switch (c)
			{
			default:
			case ZCAN:
				close (in);
				return ERROR;

			case ZSKIP:
				close (in);
				return c;

			case ZACK:
			case ZRPOS:
				break;

			case ZRINIT:
				return OK;
			}	/* switch (c) */
			
		/*
		 * 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 () returns non 0 if a character is available
		 */

		while (_rdchk ())
			{
			switch (readline (1))
				{
				case CAN:
				case ZPAD:
					c = getinsync (1);
					goto gotack;

				case XOFF:				/* Wait a while for an XON 			*/
				case XOFF | 0200:
					readline(100);
				}	/* switch (readline (1)) */
				
			}	/* while (_rdchk ()) */
			
		}	/* if (setjmp (intrjmp)) */

#if	0
	signal (SIGINT, onintr);
#endif
	newcnt = Rxbuflen;
	Txwcnt = 0;
	_stohdr (_Txpos);
	_zsbhdr (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 (Testattn)
		{
		if (--tleft)
			while (tcount < 20000)
				{
				_send ((unsigned char *) qbf, strlen (qbf));
				tcount += strlen (qbf);
				while (_rdchk ())
					{
					switch (readline (1))
						{
						case CAN:
						case ZPAD:
							goto waitack;

						case XOFF:		/* Wait for XON 					*/
						case XOFF | 0200:
							readline (100);
						}	/* switch (readline (1)) */
						
					}	/* while (_rdchk ()) */
					
				}	/* while (tcount < 20000) */
				
#if	0
		signal (SIGINT, SIG_IGN);
#endif
		_canit ();
		sleep (3);
		purgeline ();
		printf ("\nTcount = %ld\n", tcount);
		if (tleft)
			{
			printf ("ERROR: Interrupts Not Caught\n");
			return (ERROR);
			}
			
		return (0);
		}	/* if (Testattn) */
		
	do
		{
		if (Dontread)
			{
			n = Lastn;
			}
		else
			{
			n = zfilbuf (txbuf, blklen);
			Lastread = _Txpos;
			Lastn = n;
			}

		Dontread = FALSE;
		if (n < blklen)
			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)
			{
			(*_do_report) (1, &_Txpos);
			if (!sent_zm)
				{
				sent_zm = TRUE;
				_say ("ZMODEM%s", _Crc32t ? " CRC-32" : "");
				}
				
			}
					 
		_zsdata (txbuf, n, e);
		bytcnt = _Txpos += n;
		if (e == ZCRCW)
			goto waitack;

		/*
		 * 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 () returns non 0 if a character is available
		 */
		 
		while (_rdchk ())
			{
			switch (readline (1))
				{
				case CAN:
				case ZPAD:
					c = getinsync (1);
					if (c == ZACK)
						break;

					/* 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;
				}	/* switch (readline (1)) */
				
			}	/* while (_rdchk ()) */
			
		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)
					{
					_zsdata (txbuf, 0, ZCRCE);
					goto gotack;
					}
				}

			_vfile ("window = %ld", tcount);
			}

		} while (n == blklen);

#if	0
	signal (SIGINT, SIG_IGN);
#endif

	for (;;)
		{
		_stohdr (_Txpos);
		_zsbhdr (ZEOF, _Txhdr);
		switch (getinsync (0))
			{
			case ZACK:
				continue;

			case ZRPOS:
				goto somemore;

			case ZRINIT:
				return OK;

			case ZSKIP:
				close (in);
				return c;

			default:
				close (in);
				return ERROR;
			}	/* switch (getinsync (0)) */
			
		}	/* for (;;) */
		
	}	/* static int zsendfdata (void) */
	

/****************************************************************************
*	getinsync																*
*	Respond to receiver's complaint, get back in sync with receiver.		*
****************************************************************************/

static int getinsync (int flag)
	{
	int c;

	for (;;)
		{
		if (Testattn)
			{
			printf ("\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 ZTIMEOUT:
				return ERROR;

			case ZRPOS:
				/* ************************************* */
				/*	If sending to a modem buffer, you	 */
				/*	 might send a break at this point to */
				/*	 dump the modem's buffer.			 */

				if (Lastn >= 0 && Lastread == _Rxpos)
					{
					Dontread = TRUE;
					}
				else
					{
#if	0
					clearerr (in);		   /* In case file EOF seen */
#endif
					lseek (in, _Rxpos, 0);
					}

				bytcnt = Lrxpos = _Txpos = _Rxpos;
				if (Lastsync == _Rxpos)
					{
					if (++Beenhereb4 > 4)
						if (blklen > 256)
							blklen /= 2;

					}

				Lastsync = _Rxpos;
				return c;

			case ZACK:
				Lrxpos = _Rxpos;
				if (flag || _Txpos == _Rxpos)
					return ZACK;

				continue;

			case ZRINIT:
			case ZSKIP:
				close (in);
				return c;

			case ERROR:
			default:
				_zsbhdr (ZNAK, _Txhdr);
				continue;
			}	/* switch (c) */
			
		}	/* for (;;) */
		
	}	/* static int getinsync (int flag) */
	

/****************************************************************************
*	saybibi																	*
*	Say "bibi" to the receiver, try to do it cleanly.						*
****************************************************************************/

static void saybibi (void)
	{
	for (;;)
		{
		_stohdr (0L);					  /* CAF Was _zsbhdr - minor change */
		_zshhdr (ZFIN, _Txhdr);				/*	to make debugging easier */
		switch (_zgethdr (_Rxhdr, 0))
			{
			case ZFIN:
				_sendline ('O');
				_sendline ('O');
				_flushmo ();
				
			case ZCAN:
			case ZTIMEOUT:
				return;
			}

		}

	}	/* static void saybibi (void) */


/****************************************************************************
*	_bttyout																*
*	Local screen character display function.								*
****************************************************************************/
	
void _bttyout (int c)
	{
	char str[2];						/* for making string				*/
	
	if (_Verbose)
		{
		str[0] = (char) c;
		str[1] = '\0';
		_say (str);
		}
		
	}	/* void _bttyout (int c) */


/****************************************************************************
*	zsendcmd																*
*	Send command and related info.											*
****************************************************************************/

static int zsendcmd (char *buf, int blen)
	{
	int c;
	long cmdnum;

	cmdnum = getpid ();
	_errors = 0;
	for (;;)
		{
		_stohdr (cmdnum);
		_Txhdr[ZF0] = (char) Cmdack1;
		_zsbhdr (ZCOMMAND, _Txhdr);
		_zsdata (buf, blen, ZCRCW);

listen:
		_Rxtimeout = 100; 				  /* Ten second wait for resp. */
		c = _zgethdr (_Rxhdr, 1);

		switch (c)
			{
			case ZRINIT:
				goto listen;	/* CAF 8-21-87 */

			case ERROR:
			case ZTIMEOUT:
				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 = (int) _Rxpos;
				saybibi ();
				return OK;
			}	/* switch (c) */
			
		}	/* for (;;) */
		
	}	/* static int zsendcmd (char *buf, int blen) */
	

/****************************************************************************
*	chkinvok																*
*	Set chosen protocol.													*
****************************************************************************/

static void chkinvok (char protocol)
	{
	if (protocol == 'y')
		{
		_Nozmodem = TRUE;
		blklen = KSIZE;
		}
		
	if (protocol == 'x')
		{
		Modem2 = TRUE;
		}

	}	/* static void chkinvok (char protocol) */
/* End of sz.c */
