/****************************************************************************
*	Language 	:	Turbo C 2.0												*
*	Logfile		:	zm.c													*
*	Project		:	Comms Library.											*
*	Date 		:	25 Jan 90												*
*	Revision 	:	1.1		GT	PC version.									*
*	25 Oct 92	:	1.2		GT	KA9Q mods.									*
*****************************************************************************
*	Purpose		:	ZMODEM protocol primitives.								*
*****************************************************************************
*				:	This module is based on the equivalent module in the	*
*				:	31 Aug 87 version of rz/sz.								*
*	$Id: zm.c 1.1 93/01/16 18:38:59 ROOT_DOS Exp $
****************************************************************************/

/*
 *   Z M . C
 *    ZMODEM protocol primitives
 *    07-28-87  Chuck Forsberg Omen Technology Inc
 *
 * Entry point Functions:
 *	_zsbhdr (type, hdr) send binary header
 *	_zshhdr (type, hdr) send hex header
 *	_zgethdr (hdr, eflag) receive header - binary or hex
 *	_zsdata (buf, len, frameend) send data
 *	_zrdata (buf, len) receive data
 *	_stohdr (pos) store position data in _Txhdr
 *	long _rclhdr (hdr) recover position offset from header
 */

#ifndef CANFDX
#include	"zmodem.h"

int _Rxtimeout = 100;		/* Tenths of seconds to wait for something */
#endif

#include	<setjmp.h>
#include "config.h"
#include	"sz.h"
#include	"zm.h"
#include	"rbsb.h"


#ifndef UNSL
#define UNSL	unsigned
#endif


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

static int noxrd7 (void);
static int zdlread (void);
static int zgeth1 (void);
static int zgethex (void);
static void zputhex (int c);
static int zrbhdr (char *hdr);
static int zrbhdr32 (char *hdr);
static int zrdat32 (char *buf, int length);
static int zrhhdr (char *hdr);
static void zsbh32 (char *hdr, int type);
static void zsda32 (char *buf, int length, int frameend);
static void zsendline (int c);


/****************************************************************************
*	Globals used by ZMODEM functions.										*
****************************************************************************/

static int Rxframeind;		/* ZBIN ZBIN32, or ZHEX type of frame received	*/
static int Rxtype;			/* Type of header received 						*/
int _Rxcount;				/* Count of data bytes received 				*/
char _Rxhdr[4];				/* Received header 								*/
char _Txhdr[4];				/* Transmitted header 							*/
long _Rxpos;				/* Received file position 						*/
long _Txpos;				/* Transmitted file position 					*/
int _Txfcs32;				/* TRUE - send binary frames with 32 bit FCS 	*/
int _Crc32t;				/* Display flag - 32 bit CRC being sent 		*/
int _Crc32;					/* Display flag - 32 bit CRC being received		*/
int _Znulls;					/* No of nulls to send at start of ZDATA hdr	*/
char _Attn[ZATTNLEN + 1];	/* Attention string rx sends to tx on err		*/


static char *frametypes[] =
	{
	"Carrier Lost",			/* -3 */
	"TIMEOUT",				/* -2 */
	"ERROR",				/* -1 */
#define FTOFFSET 	3
	"ZRQINIT",
	"ZRINIT",
	"ZSINIT",
	"ZACK",
	"ZFILE",
	"ZSKIP",
	"ZNAK",
	"ZABORT",
	"ZFIN",
	"ZRPOS",
	"ZDATA",
	"ZEOF",
	"ZFERR",
	"ZCRC",
	"ZCHALLENGE",
	"ZCOMPL",
	"ZCAN",
	"ZFREECNT",
	"ZCOMMAND",
	"ZSTDERR",
	"xxxxx"
#define FRTYPES 22			/* Total number of frame types in this array	*/
							/*  not including psuedo negative entries		*/
	};


/****************************************************************************
*	_zsbhdr																	*
*	Send ZMODEM binary header <hdr> of type <type>.							*
****************************************************************************/

void _zsbhdr (int type, char *hdr)
	{
	int n;
	unsigned short crc;

	_vfile ("_zsbhdr: %s %lx", frametypes[type + FTOFFSET], _rclhdr (hdr));
	if (type == ZDATA)
		for (n = _Znulls; --n >= 0;)
			zsendline (0);

	_xsendline (ZPAD);
	_xsendline (ZDLE);

	if ((_Crc32t = _Txfcs32) != 0)
		zsbh32 (hdr, type);
	else
		{
		_xsendline (ZBIN);
		zsendline (type);
		crc = updcrc (type, 0);

		for (n = 4; --n >= 0; ++hdr)
			{
			zsendline (*hdr);
			crc = updcrc ((0377 & *hdr), crc);
			}
			
		crc = updcrc (0, updcrc (0, crc));
		zsendline (crc >> 8);
		zsendline (crc);
		}

	if (type != ZDATA)
		_flushmo ();

	}	/* void _zsbhdr (int type, char *hdr) */
	

/****************************************************************************
*	zsbh32																	*
*	Send ZMODEM binary header <hdr> of type <type>.							*
****************************************************************************/

static void zsbh32 (char *hdr, int type)
	{
	int n;
	UNSL long crc;

	_xsendline (ZBIN32);
	zsendline (type);
	crc = 0xFFFFFFFFL;
#ifdef	DEBUGZ
	_vfile ("\n%s:%d zsbh32 initial CRC = %08lx", __FILE__, __LINE__, crc);
#endif
	crc = UPDC32 (type, crc);
#ifdef	DEBUGZ
	_vfile ("%s:%d zsbh32 byte = %x, CRC = %08lx", __FILE__, __LINE__,
		   type, crc);
#endif

	for (n = 4; --n >= 0; ++hdr)
		{
		crc = UPDC32 ((0377 & *hdr), crc);
#ifdef	DEBUGZ
		_vfile ("%s:%d zsbh32 byte = %x, CRC = %08lx", __FILE__, __LINE__,
			   (0377 & *hdr), crc);
#endif
		zsendline (*hdr);
		}
		
	crc = ~crc;
#ifdef	DEBUGZ
	_vfile ("%s:%d zsbh32 sent CRC = %08lx", __FILE__, __LINE__, crc);
#endif
	for (n = 4; --n >= 0;)
		{
		zsendline ((int) crc);
		crc >>= 8;
		}
		
	}	/* static void zsbh32 (char *hdr, int type) */
	

/****************************************************************************
*	_zshhdr																	*
*	Send ZMODEM HEX header <hdr> of type <type>.							*
****************************************************************************/

void _zshhdr (int type, char *hdr)
	{
	int n;
	unsigned short crc;

	_vfile ("_zshhdr: %s %lx", frametypes[type + FTOFFSET], _rclhdr (hdr));
	_sendline (ZPAD);
	_sendline (ZPAD);
	_sendline (ZDLE);
	_sendline (ZHEX);
	zputhex (type);
	_Crc32t = 0;

	crc = updcrc (type, 0);
	for (n = 4; --n >= 0; ++hdr)
		{
		zputhex (*hdr);
		crc = updcrc ((0377 & *hdr), crc);
		}

	crc = updcrc (0, updcrc (0, crc));
	zputhex (crc >> 8);
	zputhex (crc);

	/* Make it printable on remote machine */

	_sendline (015);
	_sendline (012);

	/* Uncork the remote in case a fake XOFF has stopped data flow. */
	
	if (type != ZFIN && type != ZACK)
		_sendline (021);

	_flushmo ();
	}	/* void _zshhdr (int type, char *hdr) */
	

/****************************************************************************
*	_zsdata																	*
*	Send binary array <buf> of length <length>, with ending ZDLE sequence 	*
*	<frameend>.																*
****************************************************************************/

void _zsdata (char *buf, int length, int frameend)
	{
	unsigned short crc;

	_vfile ("_zsdata: length=%d end=%x", length, frameend);
	if (_Crc32t)
		zsda32 (buf, length, frameend);
	else
		{
		crc = 0;
		for (;--length >= 0; ++buf)
			{
			zsendline (*buf);
			crc = updcrc ((0377 & *buf), crc);
			}

		_xsendline (ZDLE);
		_xsendline (frameend);
		crc = updcrc (frameend, crc);

		crc = updcrc (0, updcrc (0, crc));
		zsendline (crc >> 8);
		zsendline (crc);
		}

	if (frameend == ZCRCW)
		{
		_xsendline (XON);
		_flushmo ();
		}
		
	}	/* void _zsdata (char *buf, int length, int frameend) */
	

/****************************************************************************
*	zsda32																	*
*	Send <length> bytes from <buf> followed by ZDLE <frameend> with a 32	*
*	bit CRC.																*
****************************************************************************/

static void zsda32 (char *buf, int length, int frameend)
	{
	UNSL long crc;

	crc = 0xFFFFFFFFL;
#ifdef	DEBUGZ
	_vfile ("\n%s:%d zsda32 initial CRC = %08lx", __FILE__, __LINE__, crc);
#endif
	for (;--length >= 0; ++buf)
		{
		crc = UPDC32 ((0377 & *buf), crc);
#if	0
		_vfile ("%s:%d zsda32 byte = %x, CRC = %08lx", __FILE__, __LINE__,
			   (0377 & *buf), crc);
#endif
		zsendline (*buf);
		}

	_xsendline (ZDLE);
	_xsendline (frameend);
	crc = UPDC32 (frameend, crc);
#ifdef	DEBUGZ
	_vfile ("%s:%d zsda32 byte = %x, CRC = %08lx", __FILE__, __LINE__,
		   frameend, crc);
#endif

	crc = ~crc;
#ifdef	DEBUGZ
	_vfile ("%s:%d zsda32 sent CRC = %08lx", __FILE__, __LINE__, crc);
#endif
	for (length = 4; --length >= 0;)
		{
		zsendline ((int) crc);
		crc >>= 8;
		}

	}	/* static void zsda32 (char *buf, int length, int frameend) */
	

/****************************************************************************
*	_zrdata																	*
*	Receive array <buf> of max <length> with ending ZDLE sequence and CRC.	*
*	Returns the ending character or error code.  NB: On errors may store 	*
*	<length> + 1 bytes!														*
****************************************************************************/

int _zrdata (char *buf, int length)
	{
	int c;
	unsigned short crc;
	char *end;
	int d;

	if (Rxframeind == ZBIN32)
		return zrdat32 (buf, length);

	crc = _Rxcount = 0;
	end = buf + length;
	while (buf <= end)
		{
		if ((c = zdlread ()) & ~0377)
			{
crcfoo:
		   switch (c)
				{
				case GOTCRCE:
				case GOTCRCG:
				case GOTCRCQ:
				case GOTCRCW:
					crc = updcrc (((d = c) & 0377), crc);
					if ((c = zdlread ()) & ~0377)
						goto crcfoo;

					crc = updcrc (c, crc);
					if ((c = zdlread ()) & ~0377)
						goto crcfoo;

					crc = updcrc (c, crc);
					if (crc & 0xFFFF)
						{
						_zperr_ ("Bad data CRC");
						return ERROR;
						}

					_Rxcount = (int) (length - (end - buf));
					_vfile ("_zrdata: cnt = %d ret = %x", _Rxcount, d);
					return d;

				case GOTCAN:
					_zperr_ ("Sender cancelled");
					return ZCAN;

				case ZTIMEOUT:
					_zperr_ ("TIMEOUT");
					return c;

				default:
					_zperr_ ("Bad data subpacket");
					return c;
				}	/* switch (c) */
				
			}	/* if ((c = zdlread ()) & ~0377) */
			
		*buf++ = (char) c;
		crc = updcrc (c, crc);
		}	/* while (buf <= end) */
		
	_zperr_ ("Data subpacket too long");
	return ERROR;
	}	/* int _zrdata (char *buf, int length) */
	

/****************************************************************************
*	zrdat32																	*
*	As _zrdata with 32 bit CRC.												*
****************************************************************************/

static int zrdat32 (char *buf, int length)
	{
	int c;
	UNSL long crc;
	char *end;
	int d;

	crc = 0xFFFFFFFFL;
	_Rxcount = 0;
	end = buf + length;
	while (buf <= end)
		{
		if ((c = zdlread ()) & ~0377)
			{
crcfoo:
		   switch (c)
				{
				case GOTCRCE:
				case GOTCRCG:
				case GOTCRCQ:
				case GOTCRCW:
					d = c;
					c &= 0377;
					crc = UPDC32 (c, crc);
					if ((c = zdlread ()) & ~0377)
						goto crcfoo;

					crc = UPDC32 (c, crc);
					if ((c = zdlread ()) & ~0377)
						goto crcfoo;

					crc = UPDC32 (c, crc);
					if ((c = zdlread ()) & ~0377)
						goto crcfoo;

					crc = UPDC32 (c, crc);
					if ((c = zdlread ()) & ~0377)
						goto crcfoo;

					crc = UPDC32 (c, crc);
					if (crc != 0xDEBB20E3L)
						{
						_zperr_ ("Bad data CRC");
						return ERROR;
						}

					_Rxcount = (int) (length - (end - buf));
					_vfile ("zrdat32: cnt = %d ret = %x", _Rxcount, d);
					return d;

				case GOTCAN:
					_zperr_ ("Sender cancelled");
					return ZCAN;

				case ZTIMEOUT:
					_zperr_ ("TIMEOUT");
					return c;

				default:
					_zperr_ ("Bad data subpacket");
					return c;
				}	/* switch (c) */
				
			}	/* if ((c = zdlread ()) & ~0377) */
			
		*buf++ = (char) c;
		crc = UPDC32 (c, crc);
		}	/* while (buf <= end) */
		
	_zperr_ ("Data subpacket too long");
	return ERROR;
	}	/* static int zrdat32 (char *buf, int length) */
	

/****************************************************************************
*	_zgethdr																	*
*	Read a ZMODEM header to <hdr>, either binary or hex.  <eflag> controls	*
*	local display of non zmodem characters:									*
*	0:  no display															*
*	1:  display printing characters only									*
*	2:  display all non ZMODEM characters									*
* 	On success, set Zmodem to 1, set _Rxpos and return type of header.		*
*	Otherwise return negative on error.										*
*	Return ERROR instantly if ZCRCW sequence, for fast error recovery.		*
****************************************************************************/

int _zgethdr (char *hdr, int eflag)
	{
	int c, n, cancount;

	n = _Zrwindow + _Baud_z;			/* Max bytes before start of frame	*/
	Rxframeind = Rxtype = 0;

startover:
	cancount = 5;
	
again:
	/* Return immediate ERROR if ZCRCW sequence seen */
	
	switch (c = _readline (_Rxtimeout))
		{
		case RCDO:
		case ZTIMEOUT:
			goto fifi;

		case CAN:
gotcan:
		   if (--cancount <= 0)
				{
				c = ZCAN;
				goto fifi;
				}

			switch (c = _readline (1))
				{
				case ZTIMEOUT:
					goto again;

				case ZCRCW:
					c = ERROR;

					/* **** FALL THRU TO **** */

				case RCDO:
					goto fifi;

				default:
					break;

				case CAN:
					if (--cancount <= 0)
						{
						c = ZCAN;
						goto fifi;
						}

					goto again;
				}	/* switch (c = _readline (1)) */
				
		/* **** FALL THRU TO **** */

		default:
agn2:
		   if (--n == 0)
				{
				_zperr_ ("Garbage count exceeded");
				return (ERROR);
				}

			if (eflag && ((c &= 0177) & 0140))
				_bttyout (c);
			else if (eflag > 1)
				_bttyout (c);

			goto startover;

		case ZPAD | 0200:				/* This is what we want.			*/
		case ZPAD:						/* This is what we want.			*/
			break;
		}	/* switch (c = _readline (_Rxtimeout)) */
		
	cancount = 5;

splat:
	switch (c = noxrd7 ())
		{
		case ZPAD:
			goto splat;

		case RCDO:
		case ZTIMEOUT:
			goto fifi;

		default:
			goto agn2;

		case ZDLE:						/* This is what we want.			 */
			break;
		}	/* switch (c = noxrd7 ()) */

	switch (c = noxrd7 ())
		{
		case RCDO:
		case ZTIMEOUT:
			goto fifi;

		case ZBIN:
			Rxframeind = ZBIN;
			_Crc32 = FALSE;
			c = zrbhdr (hdr);
			break;

		case ZBIN32:
			_Crc32 = Rxframeind = ZBIN32;
			c = zrbhdr32 (hdr);
			break;

		case ZHEX:
			Rxframeind = ZHEX;
			_Crc32 = FALSE;
			c = zrhhdr (hdr);
			break;

		case CAN:
			goto gotcan;

		default:
			goto agn2;
		}	/* switch (c = noxrd7 ()) */
		
	_Rxpos = hdr[ZP3] & 0377;
	_Rxpos = (_Rxpos << 8) + (hdr[ZP2] & 0377);
	_Rxpos = (_Rxpos << 8) + (hdr[ZP1] & 0377);
	_Rxpos = (_Rxpos << 8) + (hdr[ZP0] & 0377);

fifi:
	switch (c)
		{
		case GOTCAN:
			c = ZCAN;

			/* **** FALL THRU TO **** */

		case ZNAK:
		case ZCAN:
		case ERROR:
		case ZTIMEOUT:
		case RCDO:
			_zperr_ ("Got %s", frametypes[c + FTOFFSET]);

			/* **** FALL THRU TO **** */

		default:
			if (c >= -3 && c <= FRTYPES)
				_vfile ("_zgethdr: %s %lx", frametypes[c + FTOFFSET], _Rxpos);
			else
				_vfile ("_zgethdr: %d %lx", c, _Rxpos);

		}	/* switch (c) */
		
	return c;
	}	/* int _zgethdr (char *hdr, int eflag) */


/****************************************************************************
*	zrbhdr																	*
*	Receive a binary style header (type and position).						*
****************************************************************************/

static int zrbhdr (char *hdr)
	{
	int c, n;
	unsigned short crc;

	if ((c = zdlread ()) & ~0377)
		return c;

	Rxtype = c;
	crc = updcrc (c, 0);

	for (n = 4; --n >= 0; ++hdr)
		{
		if ((c = zdlread ()) & ~0377)
			return c;

		crc = updcrc (c, crc);
		*hdr = (char) c;
		}

	if ((c = zdlread ()) & ~0377)
		return c;

	crc = updcrc (c, crc);
	if ((c = zdlread ()) & ~0377)
		return c;

	crc = updcrc (c, crc);
	if (crc & 0xFFFF)
		{
		_zperr_ ("Bad Header CRC");
		return ERROR;
		}

	_Zmodem = 1;
	return Rxtype;
	}	/* static int zrbhdr (char *hdr) */
	

/****************************************************************************
*	zrbhdr32																*
*	Receive a binary style header (type and position) with 32 bit FCS.		*
****************************************************************************/

static int zrbhdr32 (char *hdr)
	{
	int c, n;
	UNSL long crc;

	if ((c = zdlread ()) & ~0377)
		return c;

	Rxtype = c;
	crc = 0xFFFFFFFFL;
	crc = UPDC32 (c, crc);
#ifdef DEBUGZ
	_vfile ("zrbhdr32 c=%X  crc=%lX", c, crc);
#endif

	for (n = 4; --n >= 0; ++hdr)
		{
		if ((c = zdlread ()) & ~0377)
			return c;

		crc = UPDC32 (c, crc);
		*hdr = (char) c;
#ifdef DEBUGZ
		_vfile ("zrbhdr32 c=%X  crc=%lX", c, crc);
#endif
		}

	for (n = 4; --n >= 0;)
		{
		if ((c = zdlread ()) & ~0377)
			return c;

		crc = UPDC32 (c, crc);
#ifdef DEBUGZ
		_vfile ("zrbhdr32 c=%X  crc=%lX", c, crc);
#endif
		}

	if (crc != 0xDEBB20E3L)
		{
		_zperr_ ("Bad Header CRC"); return ERROR;
		}

	_Zmodem = 1;
	return Rxtype;
	}	/* static int zrbhdr32 (char *hdr) */
	

/****************************************************************************
*	zrhhdr																	*
*	Receive a hex style header (type and position).							*
****************************************************************************/

static int zrhhdr (char *hdr)
	{
	int c;
	unsigned short crc;
	int n;

	if ((c = zgethex ()) < 0)
		return c;

	Rxtype = c;
	crc = updcrc (c, 0);

	for (n = 4; --n >= 0; ++hdr)
		{
		if ((c = zgethex ()) < 0)
			return c;
			
		crc = updcrc (c, crc);
		*hdr = (char) c;
		}

	if ((c = zgethex ()) < 0)
		return c;

	crc = updcrc (c, crc);
	if ((c = zgethex ()) < 0)
		return c;

	crc = updcrc (c, crc);
	if (crc & 0xFFFF)
		{
		_zperr_ ("Bad Header CRC");
		return ERROR;
		}

	if (_readline (1) == '\r')			/* Throw away possible cr/lf 		*/
		_readline (1);
		
	_Zmodem = 1;
	return Rxtype;
	}	/* static int zrhhdr (char *hdr) */
	

/****************************************************************************
*	zputhex																	*
*	Send a byte as two hex digits.											*
****************************************************************************/

static void zputhex (int c)
	{
	static char	digits[] = "0123456789abcdef";

	if (_Verbose > 8)
		_vfile ("zputhex: %02X", c);
		
	_sendline (digits[(c & 0xF0) >> 4]);
	_sendline (digits[(c) & 0xF]);
	}	/* static void zputhex (int c) */
	

/****************************************************************************
*	zsendline																*
*	Send character <c> with ZMODEM escape sequence encoding.  Escape XON,	*
*	XOFF.  Escape CR following @ (Telenet net escape).						*
****************************************************************************/

static void zsendline (int c)
	{
	static int lastsent;

	switch (c &= 0377)
		{
		case ZDLE:
			_xsendline (ZDLE);
			_xsendline (lastsent = (c ^= 0100));
			break;

		case 015:
		case 0215:
			if (!_Zctlesc && (lastsent & 0177) != '@')
				goto sendit;

			/* **** FALL THRU TO **** */

		case 020:
		case 021:
		case 023:
		case 0220:
		case 0221:
		case 0223:
			_xsendline (ZDLE);
			c ^= 0100;

sendit:
			_xsendline (lastsent = c);
			break;

		default:
			if (_Zctlesc && ! (c & 0140))
				{
				_xsendline (ZDLE);
				c ^= 0100;
				}

			_xsendline (lastsent = c);
		}	/* switch (c &= 0377) */
		
	}	/* static void zsendline (int c) */
	

/****************************************************************************
*	zgethex, zgeth1															*
*	Decode two lower case hex digits into an 8 bit byte value.				*
****************************************************************************/

static int zgethex (void)
	{
	int c;

	c = zgeth1 ();
	if (_Verbose > 8)
		_vfile ("zgethex: %02X", c);

	return c;
	}	/* static int zgethex (void) */
	
static int zgeth1 (void)
	{
	int c, n;

	if ((c = noxrd7 ()) < 0)
		return c;

	n = c - '0';
	if (n > 9)
		n -= ('a' - ':');

	if (n & ~0xF)
		return ERROR;

	if ((c = noxrd7 ()) < 0)
		return c;

	c -= '0';
	if (c > 9)
		c -= ('a' - ':');

	if (c & ~0xF)
		return ERROR;

	c += (n << 4);
	return c;
	}	/* static int zgeth1 (void) */
	

/****************************************************************************
*	zdlread																	*
*	Read a byte, checking for ZMODEM escape encoding including CAN * 5		*
*	which represents a quick abort.											*
****************************************************************************/

static int zdlread (void)
	{
	int c;

again:
	switch (c = _readline (_Rxtimeout))
		{
		case ZDLE:
			break;

		case 023:
		case 0223:
		case 021:
		case 0221:
			goto again;

		default:
			if (_Zctlesc && !(c & 0140))
				{
				goto again;
				}

			return c;
		}	/* switch (c = _readline (_Rxtimeout)) */
		
again2:
	if ((c = _readline (_Rxtimeout)) < 0)
		return c;

	if (c == CAN && (c = _readline (_Rxtimeout)) < 0)
		return c;

	if (c == CAN && (c = _readline (_Rxtimeout)) < 0)
		return c;

	if (c == CAN && (c = _readline (_Rxtimeout)) < 0)
		return c;

	switch (c)
		{
		case CAN:
			return GOTCAN;

		case ZCRCE:
		case ZCRCG:
		case ZCRCQ:
		case ZCRCW:
			return (c | GOTOR);

		case ZRUB0:
			return 0177;

		case ZRUB1:
			return 0377;

		case 023:
		case 0223:
		case 021:
		case 0221:
			goto again2;

		default:
			if (_Zctlesc && ! (c & 0140))
				{
				goto again2;
				}

			if ((c & 0140) ==  0100)
				return (c ^ 0100);

			break;
		}	/* switch (c) */
		
	_zperr_ ("Bad escape sequence %x", c);
	return ERROR;
	}	/* static int zdlread (void) */

	
/****************************************************************************
*	noxrd7																	*
*	Read a character from the modem line with timeout.  Eat parity, XON 	*
*	and XOFF characters.													*
****************************************************************************/

static int noxrd7 (void)
	{
	int c;

	for (;;)
		{
		if ((c = _readline (_Rxtimeout)) < 0)
			return c;

		switch (c &= 0177)
			{
			case XON:
			case XOFF:
				continue;

			default:
				if (_Zctlesc && !(c & 0140))
					continue;

			case '\r':
			case '\n':
			case ZDLE:
				return c;
			}	/* switch (c &= 0177) */
			
		}	/* for (;;) */
		
	}	/* static int noxrd7 (void) */
	

/****************************************************************************
*	_stohdr																	*
*	Store long integer pos in _Txhdr.										*
****************************************************************************/

void _stohdr (long pos)
	{
	_Txhdr[ZP0] = (char) pos;
	_Txhdr[ZP1] = (char) (pos >> 8);
	_Txhdr[ZP2] = (char) (pos >> 16);
	_Txhdr[ZP3] = (char) (pos >> 24);
	}	/* void _stohdr (long pos) */
	

/****************************************************************************
*	_rclhdr																	*
*	Recover a long integer from a header.									*
****************************************************************************/

long _rclhdr (char *hdr)
	{
	long l;

	l = (hdr[ZP3] & 0377);
	l = (l << 8) | (hdr[ZP2] & 0377);
	l = (l << 8) | (hdr[ZP1] & 0377);
	l = (l << 8) | (hdr[ZP0] & 0377);
	return l;
	}	/* long _rclhdr (char *hdr) */

/* End of zm.c */
