
/*% cc -g -DUNIX -DDIAGS -MAXTRIES=100 -DRXTIMEOUT=2  % -o irz; size irz;
 *
 * irz.c By Chuck Forsberg
 *  Copyright 2005 2011 Omen Technology INC All Rights Reserved
 *     This is UNPUBLISHED PROPRIETARY SOURCE CODE of Omen Technology INC.
 *     The Copyright notice above does not evidence any actual or intended
 *     publication such source code.
 *
 *  Entry:
 *	int rz();
 *		returns IZOK or IZERROR
 *
 *  External functions called:
 *
 * sendline(c)  sends a character to host
 * purgeline()	empty receive queue
 * int readline(tenths) read char from host, timeout in seconds/10
	return received byte 0 to 0377
	negative return values indicate error:
		return -1 on recoverable error
		return -2 on timeout
	trap fatal errors (loss of carrier, etc.), close file
 * sendbrk() send a break signal (not likely to be needed)
 *
 * long crprocheader(s) parse filename, decide what to do with
 *	if (zconv==ZCRESUM) means sender requests crash recovery
 * incoming file:
 *	return ERROR to skip
 *	opens it for writing
 *	Returns restart address if crash recovery desired, else 0
 *
 * putsec(s, n) write n bytes, trap errors
 *	Writing assumed to begin at the address returned by crprocheader()
 * closeit(status) finish writing 
 *	trap on error
 *	N.B: closeit might be called after the file is closed.
 * When trapping on errors, close file, call canit() unless line dropped
 */

int tryz(), rz(), rzfiles(), rzfile();
long crprocheader();
void closeit(int), putsec(), sendbrk();

#ifndef XEDSINIT
void zmputs();
#endif

#ifndef BOTH

#ifndef IZOK
#include "izm.h"
#endif


#include "icrctab.c"

#ifndef RXTIMEOUT
#define RXTIMEOUT 100
#endif
int Rxtimeout = RXTIMEOUT;

int Zrwindow = 1400;	/* RX window size (controls garbage count) */

#endif	/* BOTH */

char zconv;		/* ZMODEM file conversion request */


#ifdef UNIX
#define LOGFILE "/tmp/rzlog"
int Verbose=0;
#include "rbunix.c"	/* most of the system dependent stuff here */
#endif

#ifndef XEDSINIT
unsigned char Attn[ZATTNLEN+1];	/* Attention string rx sends to tx on err */
#endif

int Zctlesc;		/* Encode control characters */
int tryzhdrtype=ZRINIT;	/* Header type to send corresponding to Last rx close */

#ifndef BOTH
#include "izm.c"
#ifdef ADDRLE
#include "izmr.c"
#endif
#endif



#ifdef UNIX

/* called by signal interrupt or terminate to clean things up */
void bibi(n)
{
	canit(); mode(0);
	fprintf(stderr, "rz: caught signal %d; exiting", n);
	exit(128+n);
}
usage()
{
	fprintf(stderr, "Usage: irz (NO arguments)\n");
	exit(-1);
}


main(argc, argv)
char *argv[];
{

	char *p;

	if (argc != 1)
		usage();
	if ((p = getenv("VERBOSE")))
		Verbose = atoi(p);

	setbuf(stderr, NULL);
	if (Verbose) {
		if (freopen(LOGFILE, "w", stderr)==NULL) {
			printf("Can't open log file %s\n",LOGFILE);
			exit(0200);
		}
		setbuf(stderr, NULL);
	}
	mode(1);
	if (signal(SIGINT, bibi) == SIG_IGN) {
		signal(SIGINT, SIG_IGN); signal(SIGKILL, SIG_IGN);
	}
	else {
		signal(SIGINT, bibi); signal(SIGKILL, bibi);
	}
	signal(SIGTERM, bibi);
	rz();
	mode(0);
}

#endif

/*
 * Let's receive something already.
 */
int
rz()
{
	register c;

#ifdef BOTH
	Rxtimeout = 100;
#endif
	c=tryz();
	if (c) {
		if (c == ZCOMPL)
			return IZOK;
		if (c == IZERROR)
			goto fubar;
		c = rzfiles();
		if (c)
			goto fubar;
	}
	return IZOK;
fubar:
	canit();
	return IZERROR;
}

/*
 * Initialize for Zmodem receive attempt, try to activate Zmodem sender
 *  Handles ZSINIT frame
 *  Return ZFILE if Zmodem filename received, -1 on error,
 *   ZCOMPL if transaction finished,  else 0
 */
tryz()
{
	register c, n;
#ifdef LDP
	unsigned long crc;
	char *p;
#endif

	for (n=15; --n>=0; ) {
		/* Set buffer length (0) and capability flags */
		stohdr(0L);
#ifdef LDP
		Txhdr[ZF1] = CANLDP;
		/*  Overlays LSB of buffer length!! */
		Txhdr[ZF2] = Zfileclosedok ? 1:0;	/* last file was received and closed ok */
#endif
		Txhdr[ZF0] = 
#ifdef ADDRLE
		CANRLE |
#endif
			CANFC32|CANFDX|CANOVIO|CANBRK;
		if (Zctlesc)
			Txhdr[ZF0] |= TESCCTL;
		zshhdr(tryzhdrtype, Txhdr);
		if (tryzhdrtype == ZSKIP)	/* Don't skip too far */
			tryzhdrtype = ZRINIT;	/* CAF 8-21-87 */
again:
		switch (zgethdr()) {
		case ZRQINIT:
			continue;
		case ZEOF:
			continue;
		case TIMEOUT:
			continue;
		case ZFILE:
#ifdef LDP
			bytcnt = 0;
			Zldpactive = Rxhdr[ZF3] & ZSLDP;
			Zcrcpreset = crc = 0xFFFFFFFFL;
#endif
			zconv = Rxhdr[ZF0];
			tryzhdrtype = ZRINIT;
			c = zrdata(secbuf, 1024);
#ifdef UNIX
			mode(3);
#endif
			if (c == GOTCRCW) {
#ifdef LDP
				if (Zldpactive) {
					for (p=secbuf, c=Rxcount; --c >=0; ++p) {
						crc = UPDC32(*p, crc);
#ifdef DIAGS
						vfile("crc=%lX", crc);
#endif
					}
					for (p = zcpyrldp; *p; ++p)
						crc = UPDC32(*p, crc);
					Zcrcpreset = crc;
#ifdef DIAGS
					vfile("Zcrcpreset=%lX", Zcrcpreset);
#endif
				}
#endif
				return ZFILE;
			}
			zshhdr(ZNAK, Txhdr);
			goto again;
#ifndef XEDSINIT
		case ZSINIT:
			Zctlesc = TESCCTL & Rxhdr[ZF0];
			if (zrdata((char *)Attn, ZATTNLEN) == GOTCRCW) {
				stohdr(1L);
				zshhdr(ZACK, Txhdr);
				goto again;
			}
			zshhdr(ZNAK, Txhdr);
			goto again;
#endif
		case ZCOMPL:
			goto again;
		default:
			continue;
		case ZFIN:
			zshhdr(ZFIN, Txhdr);
			readline(Rxtimeout);
			readline(Rxtimeout);
			return ZCOMPL;
		case ZCAN:
			return IZERROR;
		}
	}
	return 0;
}

/*
 * Receive 1 or more files with ZMODEM protocol
 */
rzfiles()
{
	register c;

	for (;;) {
		switch (c = rzfile()) {
		case ZEOF:
		case ZSKIP:
		case ZFERR:
			switch (tryz()) {
			case ZCOMPL:
				return IZOK;
			default:
				return IZERROR;
			case ZFILE:
				break;
			}
			continue;
		default:
			return c;
		case IZERROR:
			return IZERROR;
		}
	}
}

#ifndef MAXTRIES
#define MAXTRIES 10
#endif

/*
 * Receive a file with ZMODEM protocol
 *  Assumes file name frame is in secbuf
 */
rzfile()
{
	register c, triesleft;

#ifdef LDP
	Zfileclosedok = 0;	/* last file was received and closed ok */
#endif
	bytcnt = crprocheader(secbuf);
#ifdef DIAGS
	vfile("rzfile: crprocheader returned %ld", bytcnt);
#endif
	if (bytcnt == IZERROR) {
		tryzhdrtype = ZSKIP;
#ifdef DIAGS
		vfile("rzfile: returning ZSKIP");
#endif
		return (ZSKIP);
	}
#ifdef DIAGS
	vfile("rzfile: continuing");
#endif

	triesleft = MAXTRIES; 

	for (;;) {
		stohdr(bytcnt);
		zshhdr(ZRPOS, Txhdr);
nxthdr:
		switch (c = zgethdr()) {
		default:
		case ZNAK:
		case TIMEOUT:
			if ( --triesleft < 0) {
#ifdef DIAGS
				vfile("Error count Exceeded");
				vfile("rzfile2: zgethdr returned %d", c);
#endif
				return IZERROR;
			}
			continue;
		case ZFILE:	/* ignore duplicate file packet */
			zrdata(secbuf, 1024);
			continue;
		case ZEOF:
			if (rclhdr(Rxhdr) != bytcnt) {
				/*
				 * Ignore eof if it's at wrong place - force
				 *  a timeout because the eof might have gone
				 *  out before we sent our zrpos.
				 */
				goto nxthdr;
			}
			closeit(IZOK);
#ifdef DIAGS
			vfile("rzfile3: normal EOF");
#endif
#ifdef LDP
			Zfileclosedok = 1;	/* last file was received and closed ok */
#endif
			return c;
		case IZERROR:	/* Too much garbage in header search error */
			if ( --triesleft < 0) {
#ifdef DIAGS
				vfile("Error count Exceeded");
				vfile("rzfile4: zgethdr returned %d", c);
#endif
				return IZERROR;
			}
#ifndef XEDSINIT
			zmputs(Attn);
#endif
			continue;
		case ZSKIP:
			closeit(IZERROR);
#ifdef DIAGS
			vfile("rzfile5: Sender SKIPPED file");
#endif
			return c;
		case ZDATA:
			if (rclhdr(Rxhdr) != bytcnt) {
				if ( --triesleft < 0) {
#ifdef DIAGS
					vfile("Error count Exceeded");
#endif
					return IZERROR;
				}
	/*  Put these two statements back 7-29-05 CAF */
#ifndef XEDSINIT
				zmputs(Attn);
#endif
				continue;
			}
moredata:
#ifdef UNIX
			if (Verbose>1)
				fprintf(stderr, " %7lX  ", bytcnt);
#endif
			switch (c = zrdata(secbuf, 1024))
			{
			case ZCAN:
#ifdef DIAGS
				vfile("rzfile6: zgethdr returned %d", c);
#endif
				return IZERROR;
			case IZERROR:	/* CRC error */
				if ( --triesleft < 0) {
#ifdef DIAGS
					vfile("Error count Exceeded");
					vfile("rzfile7: zgethdr returned %d", c);
#endif
					return IZERROR;
				}
#ifndef XEDSINIT
				zmputs(Attn);
#endif
				continue;
			case TIMEOUT:
				if ( --triesleft < 0) {
#ifdef DIAGS
					vfile("Error count Exceeded");
					vfile("rzfile8: zgethdr returned %d", c);
#endif
					return IZERROR;
				}
				continue;
			case GOTCRCW:
				triesleft = MAXTRIES;
				putsec(secbuf, Rxcount);
				bytcnt += Rxcount;
				stohdr(bytcnt);
				zshhdr(ZACK, Txhdr);
				sendline(XON);
				goto nxthdr;
			case GOTCRCQ:
				triesleft = MAXTRIES;
				putsec(secbuf, Rxcount);
				bytcnt += Rxcount;
				stohdr(bytcnt);
				zshhdr(ZACK, Txhdr);
				goto moredata;
			case GOTCRCG:
				triesleft = MAXTRIES;
				putsec(secbuf, Rxcount);
				bytcnt += Rxcount;
				goto moredata;
			case GOTCRCE:
				triesleft = MAXTRIES;
				putsec(secbuf, Rxcount);
				bytcnt += Rxcount;
				goto nxthdr;
			}
		}
	}
}

#ifndef XEDSINIT
/*
 * Send a string to the modem, processing for \336 (sleep 1 sec)
 *   and \335 (break signal)
 */
void
zmputs(s)
unsigned char *s;
{
	unsigned char c;

	while (*s) {
		switch (c = *s++) {
		case 0336:
#ifdef FLUSH
			flushmo();
#endif
			while (readline(1) > 0)
				;
			continue;
		case 0335:
#ifdef FLUSH
			flushmo();
#endif
			sendbrk(); continue;
		default:
			sendline(c);
		}
	}
#ifdef FLUSH
	flushmo();
#endif
}
#endif

#ifdef UNIX

/*
 * Process incoming file information header
 *  Attempts crash recovery if sender asks for it
 *  Returns 0 or file length for success, else IZERROR
 *
 */
long
crprocheader(name)
char *name;
{
	register char *p;
	long mfsize;		/* Size of incoming file */
	struct stat f;

	/* set default parameters and overrides */
	f.st_size = 0;


	if (!name || !*name)
		return IZERROR;
	strcpy(Pathname, name);
	p = name + 1 + strlen(name);
	if (*p) {			/* Y/ZMODEM file info present */
		sscanf(p, "%ld%lo", &mfsize, &Modtime);
		if (Verbose) {
			fprintf(stderr,  "Incoming header: %s\n", p);
			fprintf(stderr,  "Incoming: %s %ld\n", name, mfsize);
		}
	}

	if ((zconv==ZCRESUM) && (stat(name, &f)!= -1)) {
		vfile("Current %s is %ld", name, f.st_size);
		f.st_size &= ~511;
		fout = fopen(name, "r+");
		if ( !fout)
			return IZERROR;
		if (fseek(fout, f.st_size, 0)) {
			closeit(IZERROR);
			return IZERROR;
		}
		vfile("Crash recovery at %ld", f.st_size);
	} else
		fout = fopen(name, "w");
	if ( !fout)
		return IZERROR;
	return (f.st_size);
}
#endif	/* UNIX */



/* End of irz.c */
