/*
  zm.c - zmodem protocol handling lowlevelstuff
  Copyright (C) until 1998 Chuck Forsberg (OMEN Technology Inc)
  Copyright (C) 1996, 1997 Uwe Ohse

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2, or (at your option)
  any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
  02111-1307, USA.

  originally written by Chuck Forsberg
*/
/* historical comment: -- uwe
 *   Z M . C
 *    ZMODEM protocol primitives
 *    05-09-88  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, bytes_received) receive data
 *	stohdr(pos) store position data in Txhdr
 *	long rclhdr(hdr) recover position offset from header
 */


#include "zglobal.h"

#include <stdio.h>

unsigned int Rxtimeout = 100;		/* Tenths of seconds to wait for something */

/* Globals used by ZMODEM functions */
int Rxframeind;		/* ZBIN ZBIN32, or ZHEX type of frame received */
int Rxtype;		/* Type of header received */
char Rxhdr[4];		/* Received header */
char Txhdr[4];		/* Transmitted header */
long Txpos;		/* Transmitted file position */
int Txfcs32;		/* TRUE means send binary frames with 32 bit FCS */
int Crc32t;		/* Display flag indicating 32 bit CRC being sent */
int Crc32;		/* Display flag indicating 32 bit CRC being received */
int Znulls;		/* Number of nulls to send at beginning of ZDATA hdr */
char Attn[ZATTNLEN+1];	/* Attention string rx sends to tx on err */

static char lastsent;	/* Last char we sent */
int turbo_escape;
int bytes_per_error=0;

static const 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 */
};

#define badcrc _("Bad CRC")
/* static char *badcrc = "Bad CRC"; */
static inline int noxrd7 __P ((void));
static inline int zdlread __P ((void));
static int zdlread2 __P ((int)) LRZSZ_ATTRIB_REGPARM(1);
static inline int zgeth1 __P ((void));
static void zputhex __P ((int c, char *pos));
static inline int zgethex __P ((void));
static int zrbhdr __P ((char *hdr));
static int zrbhdr32 __P ((char *hdr));
static int zrhhdr __P ((char *hdr));
static char zsendline_tab[256];
static int zrdat32 __P ((char *buf, int length, size_t *));
static void zsbh32 __P ((char *hdr, int type));
static inline void zsendline_s __P((const char *s, size_t count));

extern int zmodem_requested;

#define sendline(c) putchar((c) & 0377)
#define xsendline(c) putchar(c)

/*
 * Read a character from the modem line with timeout.
 *  Eat parity, XON and XOFF characters.
 */
static inline int
noxrd7(void)
{
	register int c;

	for (;;) {
		if ((c = READLINE_PF(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;
		}
	}
}

static inline 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;
}

/* Decode two lower case hex digits into an 8 bit byte value */
static inline int
zgethex(void)
{
	register int c;

	c = zgeth1();
	VPRINTF(9,("zgethex: %02X", c));
	return c;
}

/*
 * Read a byte, checking for ZMODEM escape encoding
 *  including CAN*5 which represents a quick abort
 */
static inline int
zdlread(void)
{
	int c;
	/* Quick check for non control characters */
	if ((c = READLINE_PF(Rxtimeout)) & 0140)
		return c;
	return zdlread2(c);
}
/* no, i don't like gotos. -- uwe */
static int
zdlread2(int c)
{
	goto jump_over; /* bad style */

again:
	/* Quick check for non control characters */
	if ((c = READLINE_PF(Rxtimeout)) & 0140)
		return c;
jump_over:
	switch (c) {
	case ZDLE:
		break;
	case XON:
	case (XON|0200):
	case XOFF:
	case (XOFF|0200):
		goto again;
	default:
		if (Zctlesc && !(c & 0140)) {
			goto again;
		}
		return c;
	}
again2:
	if ((c = READLINE_PF(Rxtimeout)) < 0)
		return c;
	if (c == CAN && (c = READLINE_PF(Rxtimeout)) < 0)
		return c;
	if (c == CAN && (c = READLINE_PF(Rxtimeout)) < 0)
		return c;
	if (c == CAN && (c = READLINE_PF(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 XON:
	case (XON|0200):
	case XOFF:
	case (XOFF|0200):
		goto again2;
	default:
		if (Zctlesc && ! (c & 0140)) {
			goto again2;
		}
		if ((c & 0140) ==  0100)
			return (c ^ 0100);
		break;
	}
	VPRINTF(2,(_("Bad escape sequence %x"), c));
	return ERROR;
}



/*
 * Send character c with ZMODEM escape sequence encoding.
 *  Escape XON, XOFF. Escape CR following @ (Telenet net escape)
 */
inline void 
zsendline(int c)
{

	switch(zsendline_tab[(unsigned) (c&=0377)])
	{
	case 0: 
		xsendline(lastsent = c); 
		break;
	case 1:
		xsendline(ZDLE);
		c ^= 0100;
		xsendline(lastsent = c);
		break;
	case 2:
		if ((lastsent & 0177) != '@') {
			xsendline(lastsent = c);
		} else {
			xsendline(ZDLE);
			c ^= 0100;
			xsendline(lastsent = c);
		}
		break;
	}
}

static inline void 
zsendline_s(const char *s, size_t count) 
{
	const char *end=s+count;
	while(s!=end) {
		int last_esc=0;
		const char *t=s;
		while (t!=end) {
			last_esc=zsendline_tab[(unsigned) ((*t) & 0377)];
			if (last_esc) 
				break;
			t++;
		}
		if (t!=s) {
			fwrite(s,(size_t)(t-s),1,stdout);
			lastsent=t[-1];
			s=t;
		}
		if (last_esc) {
			int c=*s;
			switch(last_esc) {
			case 0: 
				xsendline(lastsent = c); 
				break;
			case 1:
				xsendline(ZDLE);
				c ^= 0100;
				xsendline(lastsent = c);
				break;
			case 2:
				if ((lastsent & 0177) != '@') {
					xsendline(lastsent = c);
				} else {
					xsendline(ZDLE);
					c ^= 0100;
					xsendline(lastsent = c);
				}
				break;
			}
			s++;
		}
	}
}


/* Send ZMODEM binary header hdr of type type */
void 
zsbhdr(int type, char *hdr)
{
	register int n;
	register unsigned short crc;

	VPRINTF(3,("zsbhdr: %s %lx", frametypes[type+FTOFFSET], rclhdr(hdr)));
	if (type == ZDATA)
		for (n = Znulls; --n >=0; )
			xsendline(0);

	xsendline(ZPAD); xsendline(ZDLE);

	Crc32t=Txfcs32;
	if (Crc32t)
		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();
}


/* Send ZMODEM binary header hdr of type type */
static void
zsbh32(char *hdr, int type)
{
	register int n;
	register unsigned long crc;

	xsendline(ZBIN32);  zsendline(type);
	crc = 0xFFFFFFFFL; crc = UPDC32(type, crc);

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

/* Send ZMODEM HEX header hdr of type type */
void 
zshhdr(int type, char *hdr)
{
	register int n;
	register unsigned short crc;
	char s[30];
	size_t len;

	VPRINTF(3,("zshhdr: %s %lx", frametypes[(type & 0x7f)+FTOFFSET], rclhdr(hdr)));
	s[0]=ZPAD;
	s[1]=ZPAD;
	s[2]=ZDLE;
	s[3]=ZHEX;
	zputhex(type & 0x7f ,s+4);
	len=6;
	Crc32t = 0;

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

	/* Make it printable on remote machine */
	s[len++]=015;
	s[len++]=0212;
	/*
	 * Uncork the remote in case a fake XOFF has stopped data flow
	 */
	if (type != ZFIN && type != ZACK)
	{
		s[len++]=021;
	}
	flushmo();
	write(1,s,len);
}

/*
 * Send binary array buf of length length, with ending ZDLE sequence frameend
 */
static const char *Zendnames[] = { "ZCRCE", "ZCRCG", "ZCRCQ", "ZCRCW"};
void 
zsdata(const char *buf, size_t length, int frameend)
{
	register unsigned short crc;

	VPRINTF(3,("zsdata: %lu %s", (unsigned long) length, 
		Zendnames[(frameend-ZCRCE)&3]));
	crc = 0;
	do {
		zsendline(*buf); crc = updcrc((0377 & *buf), crc);
		buf++;
	} while (--length>0);
	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
zsda32(const char *buf, size_t length, int frameend)
{
	int c;
	unsigned long crc;
	int i;
	VPRINTF(3,("zsdat32: %d %s", length, Zendnames[(frameend-ZCRCE)&3]));

	crc = 0xFFFFFFFFL;
	zsendline_s(buf,length);
	for (; length; length--) {
		c = *buf & 0377;
		crc = UPDC32(c, crc);
		buf++;
	}
	xsendline(ZDLE); xsendline(frameend);
	crc = UPDC32(frameend, crc);

	crc = ~crc;
	for (i=4; --i >= 0;) {
		c=(int) crc;
		if (c & 0140)
			xsendline(lastsent = c);
		else
			zsendline(c);
		crc >>= 8;
	}
	if (frameend == ZCRCW) {
		xsendline(XON);  flushmo();
	}
}

#if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ <= 4)
#  undef DEBUG_BLOCKSIZE
#endif

#ifdef DEBUG_BLOCKSIZE
struct debug_blocksize {
	int size;
	long count;
};
struct debug_blocksize blocksizes[]={
	{32,0},
	{64,0},
	{128,0},
	{256,0},
	{512,0},
	{1024,0},
	{2048,0},
	{4096,0},
	{8192,0},
	{0,0}
};
static inline void
count_blk(int size)
{
	int i;
	for (i=0;blocksizes[i].size;i++) {
		if (blocksizes[i].size==size) {
			blocksizes[i].count++;
			return;
		}
	}
	blocksizes[i].count++;
}

static void printout_blocksizes(void) __attribute__((__destructor__));
static void 
printout_blocksizes(void) 
{
	int i;
	for (i=0;blocksizes[i].size;i++) {
		if (blocksizes[i].count) {
			lsyslog(LOG_DEBUG,"%4d byte: %ld blocks\n",
				   blocksizes[i].size,blocksizes[i].count);
		}
	}
	if (blocksizes[i].count) {
		lsyslog(LOG_DEBUG,"unk. byte: %ld blocks",
			   blocksizes[i].count);
	}
}
#define COUNT_BLK(x) count_blk(x)
#else
#define COUNT_BLK(x)
#endif

/*
 * 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, size_t *bytes_received)
{
	register int c;
	register unsigned short crc;
	register char *end;
	register int d;

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

	crc = 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 = updcrc(c, 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;
					}
					*bytes_received = length - (end - buf);
					COUNT_BLK(*bytes_received);
					VPRINTF(3,("zrdata: %lu  %s", (unsigned long) (*bytes_received), 
							Zendnames[(d-GOTCRCE)&3]));
					return d;
				}
			case GOTCAN:
				zperr(_("Sender Canceled"));
				return ZCAN;
			case TIMEOUT:
				zperr(_("TIMEOUT"));
				return c;
			default:
				zperr(_("Bad data subpacket"));
				return c;
			}
		}
		*buf++ = c;
		crc = updcrc(c, crc);
	}
	zperr(_("Data subpacket too long"));
	return ERROR;
}

static int
zrdat32(char *buf, int length, size_t *bytes_received)
{
	register int c;
	register unsigned long crc;
	register char *end;
	register int d;

	crc = 0xFFFFFFFFL;  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 != 0xDEBB20E3) {
					zperr(badcrc);
					return ERROR;
				}
				*bytes_received = length - (end - buf);
				COUNT_BLK(*bytes_received);
				VPRINTF(3,("zrdat32: %lu %s", (unsigned long) *bytes_received, 
					Zendnames[(d-GOTCRCE)&3]));
				return d;
			case GOTCAN:
				zperr(_("Sender Canceled"));
				return ZCAN;
			case TIMEOUT:
				zperr(_("TIMEOUT"));
				return c;
			default:
				zperr(_("Bad data subpacket"));
				return c;
			}
		}
		*buf++ = c;
		crc = UPDC32(c, crc);
	}
	zperr(_("Data subpacket too long"));
	return ERROR;
}

/*
 * 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, size_t *Rxpos)
{
	register int c, cancount;
	unsigned int max_garbage; /* Max bytes before start of frame */
	size_t rxpos=0; /* keep gcc happy */

	max_garbage = Zrwindow + Baudrate;
	Rxframeind = Rxtype = 0;

startover:
	cancount = 5;
again:
	/* Return immediate ERROR if ZCRCW sequence seen */
	switch (c = READLINE_PF(Rxtimeout)) {
	case RCDO:
	case TIMEOUT:
		goto fifi;
	case CAN:
gotcan:
		if (--cancount <= 0) {
			c = ZCAN; goto fifi;
		}
		switch (c = READLINE_PF(1)) {
		case TIMEOUT:
			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;
		}
	/* **** FALL THRU TO **** */
	default:
agn2:
		if ( --max_garbage == 0) {
			zperr(_("Garbage count exceeded"));
			return(ERROR);
		}
		if (eflag && ((c &= 0177) & 0140) && Verbose)
			vchar(c);
		else if (eflag > 1 && Verbose)
			vchar(c);
		goto startover;
	case ZPAD|0200:		/* This is what we want. */
	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;
	}

	switch (c = noxrd7()) {
	case RCDO:
	case TIMEOUT:
		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;
	}
	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 TIMEOUT:
	case RCDO:
		zperr(_("Got %s"), frametypes[c+FTOFFSET]);
	/* **** FALL THRU TO **** */
	default:
		if (c >= -3 && c <= FRTYPES)
			VPRINTF(3,("zgethdr: %s %lx", frametypes[c+FTOFFSET], (unsigned long) rxpos));
		else
			VPRINTF(3,("zgethdr: %d %lx", c, (unsigned long) rxpos));
	}
	if (Rxpos)
		*Rxpos=rxpos;
	return c;
}

/* Receive a binary style header (type and position) */
static int 
zrbhdr(char *hdr)
{
	register int c, n;
	register 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 = 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;
	}
	protocol = ZM_ZMODEM;
	zmodem_requested=TRUE;
	return Rxtype;
}

/* Receive a binary style header (type and position) with 32 bit FCS */
static int
zrbhdr32(char *hdr)
{
	register int c, n;
	register unsigned long crc;

	if ((c = zdlread()) & ~0377)
		return c;
	Rxtype = c;
	crc = 0xFFFFFFFFL; crc = UPDC32(c, crc);
#ifdef DEBUGZ
	VPRINTF(3,("zrbhdr32 c=%X  crc=%lX", c, crc)i);
#endif

	for (n=4; --n >= 0; ++hdr) {
		if ((c = zdlread()) & ~0377)
			return c;
		crc = UPDC32(c, crc);
		*hdr = c;
#ifdef DEBUGZ
		VPRINTF(3,("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
		VPRINTF(3,("zrbhdr32 c=%X  crc=%lX", c, crc));
#endif
	}
	if (crc != 0xDEBB20E3) {
		zperr(badcrc);
		return ERROR;
	}
	protocol = ZM_ZMODEM;
	zmodem_requested=TRUE;
	return Rxtype;
}


/* Receive a hex style header (type and position) */
static 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=4; --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_PF(1)) {
	case 0215:
		/* **** FALL THRU TO **** */
	case 015:
	 	/* Throw away possible cr/lf */
		READLINE_PF(1);
		break;
	}
	protocol = ZM_ZMODEM;
	zmodem_requested=TRUE;
	return Rxtype;
}

/* Write a byte as two hex digits */
static void 
zputhex(int c, char *pos)
{
	static char	digits[]	= "0123456789abcdef";

	VPRINTF(9,("zputhex: %02X", c));
	pos[0]=digits[(c&0xF0)>>4];
	pos[1]=digits[c&0x0F];
}

void
zsendline_init(void)
{
	int i;
	for (i=0;i<256;i++) {	
		if (i & 0140)
			zsendline_tab[i]=0;
		else {
			switch(i)
			{
			case ZDLE:
			case XOFF: /* ^Q */
			case XON: /* ^S */
			case (XOFF | 0200):
			case (XON | 0200):
				zsendline_tab[i]=1;
				break;
			case 020: /* ^P */
			case 0220:
				if (turbo_escape)
					zsendline_tab[i]=0;
				else
					zsendline_tab[i]=1;
				break;
			case 015:
			case 0215:
				if (Zctlesc)
					zsendline_tab[i]=1;
				else if (!turbo_escape)
					zsendline_tab[i]=2;
				else 
					zsendline_tab[i]=0;
				break;
			default:
				if (Zctlesc)
					zsendline_tab[i]=1;
				else
					zsendline_tab[i]=0;
			}
		}
	}
}



/* Store pos in Txhdr */
void 
stohdr(size_t pos)
{
	long lpos=(long) pos;
	Txhdr[ZP0] = lpos;
	Txhdr[ZP1] = lpos>>8;
	Txhdr[ZP2] = lpos>>16;
	Txhdr[ZP3] = lpos>>24;
}

/* 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;
}

/* End of zm.c */
