/*
 * Z M . C ZMODEM protocol primitives 05-24-89	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
 *
 *
 * 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 CANFDX
#include "zmodem.h"
int 		Rxtimeout = 100;	/* Tenths of seconds to wait for something */
#endif

#ifndef UNSL
#define UNSL
#endif


/* Globals used by ZMODEM functions */
int 		Rxframeind; 		/* ZBIN ZBIN32, or ZHEX type of frame */
int 		Rxtype; 			/* Type of header received */
int 		Rxhlen; 			/* Length of header received */
int 		Rxcount;			/* Count of data bytes received */
char		Rxhdr[ZMAXHLEN];	/* Received header */
char		Txhdr[ZMAXHLEN];	/* Transmitted header */
long		Rxpos;				/* Received file position */
long		Txpos;				/* Transmitted file position */
int 		Txfcs32;			/* TURE means send binary frames with 32 bit
								 * FCS */
int 		Crc32t; 			/* Controls 32 bit CRC being sent */
								/* 1 == CRC32,	2 == CRC32 + RLE */
int 		Crc32r; 			/* Indicates/controls 32 bit CRC being received */
								/* 0 == CRC16,	1 == CRC32,  2 == CRC32 + RLE */
int 		Usevhdrs;			/* Use variable length headers */
int 		Znulls; 			/* Number of nulls to send at beginning of
								 * ZDATA hdr */
char		Attn[ZATTNLEN + 1]; /* Attention string rx sends to tx on err */
char	   *Altcan; 			/* Alternate canit string */

static		lastsent;			/* Last char we sent */
static		Not8bit;			/* Seven bits seen on header */

static char *frametypes[] =
{
	"No Response to Error Correction Request",	/* -4 */
	"No Carrier Detect",		/* -3 */
	"TIMEOUT",					/* -2 */
	"ERROR",					/* -1 */
#define FTOFFSET 4
	"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 */
};

static char badcrc[] = "Bad CRC";

/* Send ZMODEM binary header hdr of type type */
void	zsbhdr(int len, int type, register char *hdr)
{
	register int n;
	register unsigned short crc;

#ifndef DSZ
	vfile("zsbhdr: %c %d %s %lx", Usevhdrs ? 'v' : 'f', len,
		  frametypes[type + FTOFFSET], rclhdr(hdr));
#endif
	if (type == ZDATA)
	{
		for (n = Znulls; --n >= 0;)
			xsendline(0);
	}

	xsendline(ZPAD);
	xsendline(ZDLE);

	switch ( Crc32t = Txfcs32 )
	{
		case 2:
			zsbh32(len, hdr, type, Usevhdrs ? ZVBINR32 : ZBINR32);
			flushmo();
			break;
		case 1:
			zsbh32(len, hdr, type, Usevhdrs ? ZVBIN32 : ZBIN32);
			break;
		default:
			if (Usevhdrs)
			{
				xsendline(ZVBIN);
				zsendline(len);
			} else
				xsendline(ZBIN);
			zsendline(type);
			crc = updcrc(type, 0);

			for (n = len; --n >= 0; ++hdr)
			{
				zsendline( (*hdr) & 0xFF );
				crc = updcrc((0377 & *hdr), crc);
			}
			crc = updcrc(0, updcrc(0, crc));
			zsendline(crc >> 8);
			zsendline(crc);
	}
	if (type != ZDATA)
		flushmo();
}


/* Send ZMODEM binary header hdr of type type */
void	zsbh32(int len, register char *hdr, int type, int flavour)
{
	register int n;
	register UNSL long crc;

	xsendline(flavour);
	if (Usevhdrs)
		zsendline(len);
	zsendline(type);
	crc = 0xFFFF_FFFFL;
	crc = UPDC32(type, crc);

	for (n = len; --n >= 0; ++hdr)
	{
		crc = UPDC32((0377 & *hdr), crc);
		zsendline( (*hdr) & 0xFF );
	}
	crc = ~crc;
	for (n = 4; --n >= 0;)
	{
		zsendline((int) crc);
		crc >>= 8;
	}
}

/* Send ZMODEM HEX header hdr of type type */
void	zshhdr(int len, int type, register char *hdr)
{
	register int n;
	register unsigned short crc;

#ifndef DSZ
	vfile("zshhdr: %c %d %s %lx", Usevhdrs ? 'v' : 'f', len,
		  frametypes[type + FTOFFSET], rclhdr(hdr));
#endif
	sendline(ZPAD);
	sendline(ZPAD);
	sendline(ZDLE);
	if (Usevhdrs)
	{
		sendline(ZVHEX);
		zputhex(len);
	} else
		sendline(ZHEX);
	zputhex(type);
	Crc32t = 0;

	crc = updcrc(type, 0);
	for (n = len; --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(0212);
	/*
	 * Uncork the remote in case a fake XOFF has stopped data flow
	 */
	if (type != ZFIN && type != ZACK)
		sendline(021);
	flushmo();
}

/*
 * Send binary array buf of length length, with ending ZDLE sequence frameend
 */
static char *Zendnames[] = {"ZCRCE", "ZCRCG", "ZCRCQ", "ZCRCW"};
void	zsdata(register char *buf, int length, int frameend)
{
	register unsigned short crc;

#ifndef DSZ
	vfile("zsdata: %d %s", length, Zendnames[frameend - ZCRCE & 3]);
#endif
#ifdef	DEBUG
	USR_fprintf(stderr,"zsdata: Crc32t = %d\n", Crc32t );
#endif
	switch (Crc32t)
	{
		case 1:
			zsda32(buf, length, frameend);
			break;
		case 2:
			zsdar32(buf, length, frameend);
			break;
		default:
			crc = 0;
			for (; --length >= 0; ++buf)
			{
				zsendline( (*buf)&0xFF );
				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);
	if (frameend != ZCRCG)
		flushmo();
}

void	zsda32(register char *buf, int length, int frameend)
{
	register int c;
	register UNSL long crc;

	crc = 0xFFFFFFFFL;
	for (; --length >= 0; ++buf)
	{
		c = (*buf) & 0377;
		if (c & 0140)
			xsendline(lastsent = c);
		else
			zsendline(c);
		crc = UPDC32(c, crc);
	}
	xsendline(ZDLE);
	xsendline(frameend);
	crc = UPDC32(frameend, crc);

	crc = ~crc;
	for (c = 4; --c >= 0;)
	{
		zsendline((int) crc);
		crc >>= 8;
	}
}

/*
 * 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(register char *buf, int length)
{
	register int c;
	register unsigned short crc;
	register char *end;
	register int d;

	switch (Crc32r)
	{
		case 1:
			return zrdat32(buf, length);
		case 2:
			return zrdatr32(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(badcrc);
						return ERROR;
					}
					Rxcount = length - (end - buf);
#ifndef DSZ
					vfile("zrdata: %d  %s", Rxcount,
						  Zendnames[d - GOTCRCE & 3]);
#endif
					return d;
				case GOTCAN:
					zperr("Sender Canceled");
					return ZCAN;
				case TIMEOUT:
					zperr("TIMEOUT");
					return c;
				default:
					garbitch();
					return c;
			}
		}
		*buf++ = c;
		crc = updcrc(c, crc);
	}
#ifdef DSZ
	garbitch();
#else
	zperr("Data subpacket too long");
#endif
	return ERROR;
}

int		zrdat32(register char *buf, int length)
{
	register int c;
	register UNSL long crc;
	register char *end;
	register int d;

	crc = 0xFFFF_FFFFL;
	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 != 0xDEBB_20E3)
					{
						zperr(badcrc);
						return ERROR;
					}
					Rxcount = length - (end - buf);
#ifndef DSZ
					vfile("zrdat32: %d %s", Rxcount,
						  Zendnames[d - GOTCRCE & 3]);
#endif
					return d;
				case GOTCAN:
					zperr("Sender Canceled");
					return ZCAN;
				case TIMEOUT:
					zperr("TIMEOUT");
					return c;
				default:
					garbitch();
					return c;
			}
		}
		*buf++ = c;
		crc = UPDC32(c, crc);
	}
	zperr("Data subpacket too long");
	return ERROR;
}

void	garbitch(void)
{
	zperr("Garbled data subpacket");
}

/*
 * 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
 *
 * Set Rxhlen to size of header (default 4) (valid iff good hdr) 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)
{
	register int c, n, cancount;

	n = Zrwindow + Effbaud; 	/* 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 021:
		case 0221:
			goto again;
		case RCDO:
		case TIMEOUT:
			goto fifi;
		case CAN:
		  gotcan:
			if (--cancount <= 0)
			{
				c = ZCAN;
				goto fifi;
			}
			switch (c = readline(1))
			{
				case TIMEOUT:
					goto again;
				case ZCRCW:
					switch (readline(1))
					{
						case TIMEOUT:
							c = ERROR;
							goto fifi;
						case RCDO:
							goto fifi;
						default:
							goto agn2;
					}
				case RCDO:
					goto fifi;
				default:
					break;
				case CAN:
					if (--cancount <= 0)
					{
						c = ZCAN;
						goto fifi;
					}
					goto again;
			}
			/* **** FALL THRU TO **** */
		default:
		  agn2:
			if (--n == 0)
			{
				c = GCOUNT;
				goto fifi;
			}
			if (eflag && ((c &= 0177) & 0140))
				bttyout(c);
			else if (eflag > 1)
				bttyout(c);
#ifdef UNIX
			fflush(stderr);
#endif
			goto startover;
		case ZPAD | 0200:		/* This is what we want. */
			Not8bit = c;
		case ZPAD:				/* This is what we want. */
			break;
	}
	cancount = 5;
  splat:
	switch (c = noxrd7())
	{
		case ZPAD:
			goto splat;
		case RCDO:
		case TIMEOUT:
			goto fifi;
		default:
			goto agn2;
		case ZDLE:				/* This is what we want. */
			break;
	}


	Rxhlen = 4; 				/* Set default length */
	Rxframeind = c = noxrd7();
	switch (c)
	{
		case ZVBIN32:
			if ((Rxhlen = c = zdlread()) < 0)
				goto fifi;
			if (c > ZMAXHLEN)
				goto agn2;
			Crc32r = 1;
			c = zrbhd32(hdr);
			break;
		case ZBIN32:
			if (Usevhdrs)
				goto agn2;
			Crc32r = 1;
			c = zrbhd32(hdr);
			break;
		case ZVBINR32:
			if ((Rxhlen = c = zdlread()) < 0)
				goto fifi;
			if (c > ZMAXHLEN)
				goto agn2;
			Crc32r = 2;
			c = zrbhd32(hdr);
			break;
		case ZBINR32:
			if (Usevhdrs)
				goto agn2;
			Crc32r = 2;
			c = zrbhd32(hdr);
			break;
		case RCDO:
		case TIMEOUT:
			goto fifi;
		case ZVBIN:
			if ((Rxhlen = c = zdlread()) < 0)
				goto fifi;
			if (c > ZMAXHLEN)
				goto agn2;
			Crc32r = 0;
			c = zrbhdr(hdr);
			break;
		case ZBIN:
			if (Usevhdrs)
				goto agn2;
			Crc32r = 0;
			c = zrbhdr(hdr);
			break;
		case ZVHEX:
			if ((Rxhlen = c = zgethex()) < 0)
				goto fifi;
			if (c > ZMAXHLEN)
				goto agn2;
			Crc32r = 0;
			c = zrhhdr(hdr);
			break;
		case ZHEX:
			if (Usevhdrs)
				goto agn2;
			Crc32r = 0;
			c = zrhhdr(hdr);
			break;
		case CAN:
			goto gotcan;
		default:
			goto agn2;
	}
	for (n = Rxhlen; ++n < ZMAXHLEN;)	/* Clear unused hdr bytes */
		hdr[n] = 0;
	Rxpos = hdr[ZP3] & 0377;
	Rxpos = (Rxpos << 8) + (hdr[ZP2] & 0377);
	Rxpos = (Rxpos << 8) + (hdr[ZP1] & 0377);
	Rxpos = (Rxpos << 8) + (hdr[ZP0] & 0377);
#ifdef	DEBUG
	USR_fprintf(stderr,"Rxpos = %10ld (hdr = %02X %02X %02X %02X)\n",
	    Rxpos, hdr[0], hdr[1], hdr[2], hdr[3] );
#endif
  fifi:
	switch (c)
	{
		case GOTCAN:
			c = ZCAN;
			/* **** FALL THRU TO **** */
		case ZNAK:
		case ZCAN:
		case ERROR:
		case TIMEOUT:
		case RCDO:
		case GCOUNT:
			zperr("Got %s", frametypes[c + FTOFFSET]);
			/* **** FALL THRU TO **** */
#ifndef DSZ
		default:
			if (c >= -4 && c <= FRTYPES)
				vfile("zgethdr: %c %d %s %lx", Rxframeind, Rxhlen,
					  frametypes[c + FTOFFSET], Rxpos);
			else
				vfile("zgethdr: %c %d %lx", Rxframeind, c, Rxpos);
#endif
	}
	/* Use variable length headers if we got one */
	if (c >= 0 && c <= FRTYPES && Rxframeind & 040)
		Usevhdrs = 1;
	return c;
}

/* Receive a binary style header (type and position) */
int		zrbhdr(register char *hdr)
{
	register int c, n;
	register unsigned short crc;

	if ((c = zdlread()) & ~0377)
		return c;
	Rxtype = c;
	crc = updcrc(c, 0);

	for (n = Rxhlen; --n >= 0; ++hdr)
	{
		if ((c = zdlread()) & ~0377)
			return c;
		crc = updcrc(c, crc);
		*hdr = 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(badcrc);
		return ERROR;
	}
#ifdef ZMODEM
	Protocol = ZMODEM;
#endif
	Zmodem = 1;
	return Rxtype;
}

/* Receive a binary style header (type and position) with 32 bit FCS */
int		zrbhd32(register char *hdr)
{
	register int c, n;
	register UNSL long crc;

	if ((c = zdlread()) & ~0377)
		return c;
	Rxtype = c;
	crc = 0xFFFFFFFFL;
	crc = UPDC32(c, crc);
#ifdef DEBUGZ
	vfile("zrbhd32 c=%X  crc=%lX", c, crc);
#endif

	for (n = Rxhlen; --n >= 0; ++hdr)
	{
		if ((c = zdlread()) & ~0377)
			return c;
		crc = UPDC32(c, crc);
		*hdr = c;
#ifdef DEBUGZ
		vfile("zrbhd32 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("zrbhd32 c=%X  crc=%lX", c, crc);
#endif
	}
	if (crc != 0xDEBB20E3)
	{
		zperr(badcrc);
		return ERROR;
	}
#ifdef ZMODEM
	Protocol = ZMODEM;
#endif
	Zmodem = 1;
	return Rxtype;
}


/* Receive a hex style header (type and position) */
int		zrhhdr(char *hdr)
{
	register int c;
	register unsigned short crc;
	register int n;

	if ((c = zgethex()) < 0)
		return c;
	Rxtype = c;
	crc = updcrc(c, 0);

	for (n = Rxhlen; --n >= 0; ++hdr)
	{
		if ((c = zgethex()) < 0)
			return c;
		crc = updcrc(c, crc);
		*hdr = 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(badcrc);
		return ERROR;
	}
	switch (c = readline(2))
	{
		case 0215:
			Not8bit = c;
			/* **** FALL THRU TO **** */
		case 015:
			/* Throw away possible cr/lf */
			switch (c = readline(2))
			{
				case 012:
					Not8bit |= c;
			}
	}
#ifdef ZMODEM
	Protocol = ZMODEM;
#endif
	Zmodem = 1;
	if (c < 0)
		return c;
	return Rxtype;
}

/* Send a byte as two hex digits */
void	zputhex(register int c)
{
	static char digits[] = "0123456789abcdef";

#ifdef DEBUGZ
	if (Verbose > 8)
		vfile("zputhex: %02X", c);
#endif
	sendline(digits[(c & 0xF0) >> 4]);
	sendline(digits[(c) & 0xF]);
}

/*
 * Send character c with ZMODEM escape sequence encoding. Escape XON, XOFF.
 * Escape CR following @ (Telenet net escape)
 */
void	zsendline(int c)
{
	c &= 0xFF;
	/* Quick check for non control characters */
	if (c & 0140)
		xsendline(lastsent = c);
	else
	{
		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);
		}
	}
}

/* Decode two lower case hex digits into an 8 bit byte value */
int		zgethex(void)
{
	register int c;

	c = zgeth1();
#ifdef DEBUGZ
	if (Verbose > 8)
		vfile("zgethex: %02X", c);
#endif
	return c;
}
int		zgeth1(void)
{
	register 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;
}

/*
 * Read a byte, checking for ZMODEM escape encoding including CAN*5 which
 * represents a quick abort
 */
int		zdlread(void)
{
	register int c;

  again:
	/* Quick check for non control characters */
	if ((c = readline(Rxtimeout)) & 0140)
		return c;
	switch (c)
	{
		case ZDLE:
			break;
		case 023:
		case 0223:
		case 021:
		case 0221:
			goto again;
		default:
			if (Zctlesc && !(c & 0140))
			{
				goto again;
			}
			return c;
	}
  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;
	}
	if (Verbose > 1)
		zperr("Bad escape sequence %x", c);
	return ERROR;
}

/*
 * Read a character from the modem line with timeout. Eat parity, XON and
 * XOFF characters.
 */
int		noxrd7(void)
{
	register 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;
		}
	}
}

/* Store long integer pos in Txhdr */
void	stohdr(long pos)
{
	Txhdr[ZP0] = pos;
	Txhdr[ZP1] = pos >> 8;
	Txhdr[ZP2] = pos >> 16;
	Txhdr[ZP3] = pos >> 24;
}

/* Recover a long integer from a header */
long	rclhdr(register char *hdr)
{
	register 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;
}

/* End of zm.c */
