[comp.sources.atari.st] v01i039: rzsz -- UNIX side for zmdm file transfer program part04/05

koreth@ssyx.ucsc.edu (Steven Grimm) (05/28/88)

Submitted-by: bammi@mandrill.ces.cwru.edu (Jwahar R. Bammi)
Posting-number: Volume 1, Issue 39
Archive-name: rzsz/part04

#!/bin/sh
# this is part 4 of a multipart archive
# do not concatenate these parts, unpack them in order with /bin/sh
# file sz.c continued
#
CurArch=4
if test ! -r s2_seq_.tmp
then echo "Please unpack part 1 first!"
     exit 1; fi
( read Scheck
  if test "$Scheck" != $CurArch
  then echo "Please unpack part $Scheck next!"
       exit 1;
  else exit 0; fi
) < s2_seq_.tmp || exit 1
sed 's/^X//' << 'SHAR_EOF' >> sz.c
Xchar Lzconv;	/* Local ZMODEM file conversion request */
Xchar Lzmanag;	/* Local ZMODEM file management request */
Xchar Lztrans;
Xchar zconv;		/* ZMODEM file conversion request */
Xchar zmanag;		/* ZMODEM file management request */
Xchar ztrans;		/* ZMODEM file transport request */
Xint Command;		/* Send a command, then exit. */
Xchar *Cmdstr;		/* Pointer to the command string */
Xint Cmdtries = 11;
Xint Cmdack1;		/* Rx ACKs command, then do it */
Xint Exitcode;
Xint Testattn;		/* Force receiver to send Attn, etc with qbf. */
Xchar *qbf="The quick brown fox jumped over the lazy dog's back 1234567890\r\n";
Xlong Lastread;		/* Beginning offset of last buffer read */
Xint Lastc;		/* Count of last buffer read or -1 */
Xint Dontread;		/* Don't read the buffer, it's still there */
X
Xjmp_buf tohere;		/* For the interrupt on RX timeout */
Xjmp_buf intrjmp;	/* For the interrupt on RX CAN */
X
X/* called by signal interrupt or terminate to clean things up */
Xbibi(n)
X{
X	canit(); fflush(stdout); mode(0);
X	fprintf(stderr, "sz: caught signal %d; exiting\n", n);
X	if (n == SIGQUIT)
X		abort();
X	exit(128+n);
X}
X/* Called when Zmodem gets an interrupt (^X) */
Xonintr()
X{
X	signal(SIGINT, SIG_IGN);
X	longjmp(intrjmp, -1);
X}
X
X
X#define sendline(c) putchar(c & Wcsmask)
X
X#define xsendline(c) putchar(c)
X
Xflushmo()
X{
X	fflush(stdout);
X}
X
X#define ZKER
Xint Zctlesc;	/* Encode control characters */
Xint Nozmodem = 0;	/* If invoked as "sb" */
Xchar *Progname = "sz";
X#include "zm.c"
X
X
X#ifdef CASE
XMAIN(argc, argv)
X#else
Xmain(argc, argv)
X#endif
Xint argc;
Xchar **argv;
X{
X	register char *cp;
X	register npats;
X	int agcnt; char **agcv;
X	char **patts;
X	static char xXbuf[BUFSIZ];
X
X	if ((cp = getenv("ZNULLS")) && *cp)
X		Znulls = atoi(cp);
X	if ((cp=getenv("SHELL")) && (substr(cp, "rsh") || substr(cp, "rksh")))
X		Restricted=TRUE;
X	chkinvok(argv[0]);
X
X	Rxtimeout = 600;
X	npats=0;
X	if (argc<2)
X		usage();
X	setbuf(stdout, xXbuf);		
X	while (--argc) {
X		cp = *++argv;
X		if (*cp++ == '-' && *cp) {
X			while ( *cp) {
X				switch(*cp++) {
X				case '+':
X					Lzmanag = ZMAPND; break;
X				case '1':
X					iofd = 1; break;
X#ifdef CSTOPB
X				case '2':
X					Twostop = TRUE; break;
X#endif
X				case '7':
X					Wcsmask=0177; break;
X				case 'a':
X					Lzconv = ZCNL;
X					Ascii = TRUE; break;
X				case 'b':
X					Lzconv = ZCBIN; break;
X				case 'C':
X					if (--argc < 1) {
X						usage();
X					}
X					Cmdtries = atoi(*++argv);
X					break;
X				case 'i':
X					Cmdack1 = ZCACK1;
X					/* **** FALL THROUGH TO **** */
X				case 'c':
X					if (--argc != 1) {
X						usage();
X					}
X					Command = TRUE;
X					Cmdstr = *++argv;
X					break;
X				case 'd':
X					++Dottoslash;
X					/* **** FALL THROUGH TO **** */
X				case 'f':
X					Fullname=TRUE; break;
X				case 'E':
X					Zctlesc = -1; break;
X				case 'e':
X					Zctlesc = 1; break;
X				case 'k':
X					blklen=KSIZE; break;
X				case 'L':
X					if (--argc < 1) {
X						usage();
X					}
X					blkopt = atoi(*++argv);
X					if (blkopt<24 || blkopt>1024)
X						usage();
X					break;
X				case 'l':
X					if (--argc < 1) {
X						usage();
X					}
X					Tframlen = atoi(*++argv);
X					if (Tframlen<32 || Tframlen>1024)
X						usage();
X					break;
X				case 'N':
X					Lzmanag = ZMDIFF;  break;
X				case 'n':
X					Lzmanag = ZMNEW;  break;
X				case 'o':
X					Wantfcs32 = FALSE; break;
X				case 'p':
X					Lzmanag = ZMPROT;  break;
X				case 'r':
X					Lzconv = ZCRESUM;
X				case 'q':
X					Quiet=TRUE; Verbose=0; break;
X				case 't':
X					if (--argc < 1) {
X						usage();
X					}
X					Rxtimeout = atoi(*++argv);
X					if (Rxtimeout<10 || Rxtimeout>1000)
X						usage();
X					break;
X				case 'T':
X					Testattn = TRUE; break;
X				case 'u':
X					++Unlinkafter; break;
X				case 'v':
X					++Verbose; break;
X				case 'X':
X					++Modem; break;
X				case 'y':
X					Lzmanag = ZMCLOB; break;
X				default:
X					usage();
X				}
X			}
X		}
X		else if ( !npats && argc>0) {
X			if (argv[0][0]) {
X				npats=argc;
X				patts=argv;
X				if ( !strcmp(*patts, "-"))
X					iofd = 1;
X			}
X		}
X	}
X	if (npats < 1 && !Command) 
X		usage();
X	if (Verbose) {
X		if (freopen(LOGFILE, "a", stderr)==NULL) {
X			printf("Can't open log file %s\n",LOGFILE);
X			exit(0200);
X		}
X		setbuf(stderr, NULL);
X	}
X	if ((Fromcu=from_cu()) && !Quiet) {
X		if (Verbose == 0)
X			Verbose = 2;
X	}
X
X	mode(1);
X
X	if (signal(SIGINT, bibi) == SIG_IGN) {
X		signal(SIGINT, SIG_IGN); signal(SIGKILL, SIG_IGN);
X	} else {
X		signal(SIGINT, bibi); signal(SIGKILL, bibi);
X	}
X	if ( !Fromcu)
X		signal(SIGQUIT, SIG_IGN);
X
X	if ( !Modem) {
X		if (!Nozmodem) {
X			printf("rz\r");  fflush(stdout);
X		}
X		if (!Command && !Quiet && Verbose != 1) {
X			fprintf(stderr, "%s: %d file%s requested:\r\n",
X			 Progname, npats, npats>1?"s":"");
X			for ( agcnt=npats, agcv=patts; --agcnt>=0; ) {
X				fprintf(stderr, "%s ", *agcv++);
X			}
X			fprintf(stderr, "\r\n");
X			printf("\r\n\bSending in Batch Mode\r\n");
X		}
X		if (!Nozmodem) {
X			stohdr(0L);
X			if (Command)
X				Txhdr[ZF0] = ZCOMMAND;
X			zshhdr(ZRQINIT, Txhdr);
X		}
X	}
X	fflush(stdout);
X
X	if (Command) {
X		if (getzrxinit()) {
X			Exitcode=0200; canit();
X		}
X		else if (zsendcmd(Cmdstr, 1+strlen(Cmdstr))) {
X			Exitcode=0200; canit();
X		}
X	} else if (wcsend(npats, patts)==ERROR) {
X		Exitcode=0200;
X		canit();
X	}
X	fflush(stdout);
X	mode(0);
X	exit((errcnt != 0) | Exitcode);
X	/*NOTREACHED*/
X}
X
Xwcsend(argc, argp)
Xchar *argp[];
X{
X	register n;
X
X	Crcflg=FALSE;
X	firstsec=TRUE;
X	for (n=0; n<argc; ++n) {
X		Totsecs = 0;
X		if (wcs(argp[n])==ERROR)
X			return ERROR;
X	}
X	Totsecs = 0;
X	if (Filcnt==0) {	/* bitch if we couldn't open ANY files */
X		if (1) {
X			Command = TRUE;
X			Cmdstr = "echo \"sz: Can't open any requested files\"";
X			if (getnak()) {
X				Exitcode=0200; canit();
X			}
X			if (!Zmodem)
X				canit();
X			else if (zsendcmd(Cmdstr, 1+strlen(Cmdstr))) {
X				Exitcode=0200; canit();
X			}
X			Exitcode = 1; return OK;
X		}
X		canit();
X		fprintf(stderr,"\r\nCan't open any requested files.\r\n");
X		return ERROR;
X	}
X	if (Zmodem)
X		saybibi();
X	else
X		wctxpn("");
X	return OK;
X}
X
Xwcs(oname)
Xchar *oname;
X{
X	register c;
X	register char *p;
X	struct stat f;
X	char name[PATHLEN];
X
X	strcpy(name, oname);
X
X	if (Restricted) {
X		/* restrict pathnames to current tree or uucppublic */
X		if ( substr(name, "../")
X		 || (name[0]== '/' && strncmp(name, PUBDIR, strlen(PUBDIR))) ) {
X			canit();
X			fprintf(stderr,"\r\nsz:\tSecurity Violation\r\n");
X			return ERROR;
X		}
X	}
X
X	if ( !strcmp(oname, "-")) {
X		if ((p = getenv("ONAME")) && *p)
X			strcpy(name, p);
X		else
X			sprintf(name, "s%d.sz", getpid());
X		in = stdin;
X	}
X	else if ((in=fopen(oname, "r"))==NULL) {
X		++errcnt;
X		return OK;	/* pass over it, there may be others */
X	}
X	++Noeofseen;  Lastread = 0;  Lastc = -1; Dontread = FALSE;
X	/* Check for directory or block special files */
X	fstat(fileno(in), &f);
X	c = f.st_mode & S_IFMT;
X	if (c == S_IFDIR || c == S_IFBLK) {
X		fclose(in);
X		return OK;
X	}
X
X	++Filcnt;
X	switch (wctxpn(name)) {
X	case ERROR:
X		return ERROR;
X	case ZSKIP:
X		return OK;
X	}
X	if (!Zmodem && wctx()==ERROR)
X		return ERROR;
X	if (Unlinkafter)
X		unlink(oname);
X	return 0;
X}
X
X/*
X * generate and transmit pathname block consisting of
X *  pathname (null terminated),
X *  file length, mode time and file mode in octal
X *  as provided by the Unix fstat call.
X *  N.B.: modifies the passed name, may extend it!
X */
Xwctxpn(name)
Xchar *name;
X{
X	register char *p, *q;
X	char name2[PATHLEN];
X	struct stat f;
X
X	if (Modem) {
X#ifdef CASE
X		if (Verbose && (in!=stdin) && *name && fstat(fileno(in), &f)!= -1) {
X#else
X		if ((in!=stdin) && *name && fstat(fileno(in), &f)!= -1) {
X#endif
X			fprintf(stderr, "Sending %s, %ld blocks: ",
X			  name, f.st_size>>7);
X		}
X#ifdef CASE
X		if(Verbose)
X#endif
X		    fprintf(stderr, "Give your local XMODEM receive command now.\r\n");
X		return OK;
X	}
X	logent("\r\nAwaiting pathname nak for %s\r\n", *name?name:"<END>");
X	if ( !Zmodem)
X		if (getnak())
X			return ERROR;
X
X	q = (char *) 0;
X	if (Dottoslash) {		/* change . to . */
X		for (p=name; *p; ++p) {
X			if (*p == '/')
X				q = p;
X			else if (*p == '.')
X				*(q=p) = '/';
X		}
X		if (q && strlen(++q) > 8) {	/* If name>8 chars */
X			q += 8;			/*   make it .ext */
X			strcpy(name2, q);	/* save excess of name */
X			*q = '.';
X			strcpy(++q, name2);	/* add it back */
X		}
X	}
X
X	for (p=name, q=txbuf ; *p; )
X		if ((*q++ = *p++) == '/' && !Fullname)
X			q = txbuf;
X	*q++ = 0;
X	p=q;
X	while (q < (txbuf + KSIZE))
X		*q++ = 0;
X	if (!Ascii && (in!=stdin) && *name && fstat(fileno(in), &f)!= -1)
X		sprintf(p, "%lu %lo %o", f.st_size, f.st_mtime, f.st_mode);
X	/* force 1k blocks if name won't fit in 128 byte block */
X	if (txbuf[125])
X		blklen=KSIZE;
X	else {		/* A little goodie for IMP/KMD */
X		if (Zmodem)
X			blklen = SECSIZ;
X		txbuf[127] = (f.st_size + 127) >>7;
X		txbuf[126] = (f.st_size + 127) >>15;
X	}
X	if (Zmodem)
X		return zsendfile(txbuf, 1+strlen(p)+(p-txbuf));
X	if (wcputsec(txbuf, 0, SECSIZ)==ERROR)
X		return ERROR;
X	return OK;
X}
X
Xgetnak()
X{
X	register firstch;
X
X	Lastrx = 0;
X	for (;;) {
X		switch (firstch = readock(800,1)) {
X		case ZPAD:
X			if (getzrxinit())
X				return ERROR;
X			Ascii = 0;
X			return FALSE;
X		case TIMEOUT:
X			logent("Timeout on pathname\n");
X			return TRUE;
X		case WANTG:
X#ifdef USG
X			mode(2);	/* Set cbreak, XON/XOFF, etc. */
X#endif
X			Optiong = TRUE;
X			blklen=KSIZE;
X		case WANTCRC:
X			Crcflg = TRUE;
X		case NAK:
X			return FALSE;
X		case CAN:
X			if ((firstch = readock(20,1)) == CAN && Lastrx == CAN)
X				return TRUE;
X		default:
X			break;
X		}
X		Lastrx = firstch;
X	}
X}
X
X
Xwctx()
X{
X	register int sectnum, attempts, firstch;
X
X	firstsec=TRUE;
X
X	while ((firstch=readock(Rxtimeout, 2))!=NAK && firstch != WANTCRC
X	  && firstch != WANTG && firstch!=TIMEOUT && firstch!=CAN)
X		;
X	if (firstch==CAN) {
X		logent("Receiver CANcelled\n");
X		return ERROR;
X	}
X	if (firstch==WANTCRC)
X		Crcflg=TRUE;
X	if (firstch==WANTG)
X		Crcflg=TRUE;
X	sectnum=1;
X	while (filbuf(txbuf, blklen)) {
X		if (wcputsec(txbuf, sectnum, blklen)==ERROR) {
X			return ERROR;
X		} else
X			sectnum++;
X	}
X	if (Verbose>1)
X		fprintf(stderr, " Closing ");
X	fclose(in);
X	attempts=0;
X	do {
X		logent(" EOT ");
X		purgeline();
X		sendline(EOT);
X		fflush(stdout);
X		++attempts;
X	}
X		while ((firstch=(readock(Rxtimeout, 1)) != ACK) && attempts < RETRYMAX);
X	if (attempts == RETRYMAX) {
X		logent("No ACK on EOT\n");
X		return ERROR;
X	}
X	else
X		return OK;
X}
X
Xwcputsec(buf, sectnum, cseclen)
Xchar *buf;
Xint sectnum;
Xint cseclen;	/* data length of this sector to send */
X{
X	register checksum, wcj;
X	register char *cp;
X	unsigned oldcrc;
X	int firstch;
X	int attempts;
X
X	firstch=0;	/* part of logic to detect CAN CAN */
X
X	if (Verbose>1)
X		fprintf(stderr, "\rSector %3d %2dk ", Totsecs, Totsecs/8 );
X	for (attempts=0; attempts <= RETRYMAX; attempts++) {
X		Lastrx= firstch;
X		sendline(cseclen==KSIZE?STX:SOH);
X		sendline(sectnum);
X		sendline(-sectnum -1);
X		oldcrc=checksum=0;
X		for (wcj=cseclen,cp=buf; --wcj>=0; ) {
X			sendline(*cp);
X			oldcrc=updcrc((0377& *cp), oldcrc);
X			checksum += *cp++;
X		}
X		if (Crcflg) {
X			oldcrc=updcrc(0,updcrc(0,oldcrc));
X			sendline((int)oldcrc>>8);
X			sendline((int)oldcrc);
X		}
X		else
X			sendline(checksum);
X
X		if (Optiong) {
X			firstsec = FALSE; return OK;
X		}
X		firstch = readock(Rxtimeout, (Noeofseen&&sectnum) ? 2:1);
Xgotnak:
X		switch (firstch) {
X		case CAN:
X			if(Lastrx == CAN) {
Xcancan:
X				logent("Cancelled\n");  return ERROR;
X			}
X			break;
X		case TIMEOUT:
X			logent("Timeout on sector ACK\n"); continue;
X		case WANTCRC:
X			if (firstsec)
X				Crcflg = TRUE;
X		case NAK:
X			logent("NAK on sector\n"); continue;
X		case ACK: 
X			firstsec=FALSE;
X			Totsecs += (cseclen>>7);
X			return OK;
X		case ERROR:
X			logent("Got burst for sector ACK\n"); break;
X		default:
X			logent("Got %02x for sector ACK\n", firstch); break;
X		}
X		for (;;) {
X			Lastrx = firstch;
X			if ((firstch = readock(Rxtimeout, 2)) == TIMEOUT)
X				break;
X			if (firstch == NAK || firstch == WANTCRC)
X				goto gotnak;
X			if (firstch == CAN && Lastrx == CAN)
X				goto cancan;
X		}
X	}
X	logent("Retry Count Exceeded\n");
X	return ERROR;
X}
X
X/* fill buf with count chars padding with ^Z for CPM */
Xfilbuf(buf, count)
Xregister char *buf;
X{
X	register c, m;
X
X	if ( !Ascii) {
X		m = read(fileno(in), buf, count);
X		if (m <= 0)
X			return 0;
X		while (m < count)
X			buf[m++] = 032;
X		return count;
X	}
X	m=count;
X	if (Lfseen) {
X		*buf++ = 012; --m; Lfseen = 0;
X	}
X	while ((c=getc(in))!=EOF) {
X		if (c == 012) {
X			*buf++ = 015;
X			if (--m == 0) {
X				Lfseen = TRUE; break;
X			}
X		}
X		*buf++ =c;
X		if (--m == 0)
X			break;
X	}
X	if (m==count)
X		return 0;
X	else
X		while (--m>=0)
X			*buf++ = CPMEOF;
X	return count;
X}
X/* fill buf with count chars */
Xzfilbuf(buf, count)
Xregister char *buf;
X{
X	register c, m;
X
X	m=count;
X	while ((c=getc(in))!=EOF) {
X		*buf++ =c;
X		if (--m == 0)
X			break;
X	}
X	return (count - m);
X}
X
X/* VARARGS1 */
Xvfile(f, a, b, c)
Xregister char *f;
X{
X	if (Verbose > 1) {
X		fprintf(stderr, f, a, b, c);
X		fprintf(stderr, "\n");
X	}
X}
X
X
Xalrm()
X{
X	longjmp(tohere, -1);
X}
X
X
X/*
X * readock(timeout, count) reads character(s) from file descriptor 0
X *  (1 <= count <= 3)
X * it attempts to read count characters. If it gets more than one,
X * it is an error unless all are CAN
X * (otherwise, only normal response is ACK, CAN, or C)
X *  Only looks for one if Optiong, which signifies cbreak, not raw input
X *
X * timeout is in tenths of seconds
X */
Xreadock(timeout, count)
X{
X	register int c;
X	static char byt[5];
X
X	if (Optiong)
X		count = 1;	/* Special hack for cbreak */
X
X	fflush(stdout);
X	if (setjmp(tohere)) {
X		logent("TIMEOUT\n");
X		return TIMEOUT;
X	}
X	c = timeout/10;
X	if (c<2)
X		c=2;
X	if (Verbose>3) {
X		fprintf(stderr, "Timeout=%d Calling alarm(%d) ", timeout, c);
X		byt[1] = 0;
X	}
X	signal(SIGALRM, alrm); alarm(c);
X#ifdef ONEREAD
X	c=read(iofd, byt, 1);		/* regulus raw read is unique */
X#else
X	c=read(iofd, byt, count);
X#endif
X	alarm(0);
X	if (Verbose>5)
X		fprintf(stderr, "ret cnt=%d %x %x\n", c, byt[0], byt[1]);
X	if (c<1)
X		return TIMEOUT;
X	if (c==1)
X		return (byt[0]&0377);
X	else
X		while (c)
X			if (byt[--c] != CAN)
X				return ERROR;
X	return CAN;
X}
Xreadline(n)
X{
X	return (readock(n, 1));
X}
X
X
Xpurgeline()
X{
X#ifdef USG
X	ioctl(iofd, TCFLSH, 0);
X#else
X	lseek(iofd, 0L, 2);
X#endif
X}
X
X
X/* send cancel string to get the other end to shut up */
Xcanit()
X{
X	static char canistr[] = {
X	 24,24,24,24,24,24,24,24,24,24,8,8,8,8,8,8,8,8,8,8,0
X	};
X
X	printf(canistr);
X	fflush(stdout);
X}
X
X/*VARARGS1*/
Xlogent(a, b, c)
Xchar *a, *b, *c;
X{
X	if(Verbose)
X		fprintf(stderr, a, b, c);
X}
X
X/*
X * return 1 iff stdout and stderr are different devices
X *  indicating this program operating with a modem on a
X *  different line
X */
Xfrom_cu()
X{
X	struct stat a, b;
X	fstat(1, &a); fstat(2, &b);
X	return (a.st_rdev != b.st_rdev);
X}
X
X/*
X * substr(string, token) searches for token in string s
X * returns pointer to token within string if found, NULL otherwise
X */
Xchar *
Xsubstr(s, t)
Xregister char *s,*t;
X{
X	register char *ss,*tt;
X	/* search for first char of token */
X	for (ss=s; *s; s++)
X		if (*s == *t)
X			/* compare token with substring */
X			for (ss=s,tt=t; ;) {
X				if (*tt == 0)
X					return s;
X				if (*ss++ != *tt++)
X					break;
X			}
X	return NULL;
X}
X
Xchar *babble[] = {
X	"Send file(s) with ZMODEM/YMODEM/XMODEM Protocol",
X	"	(Y) = Option applies to YMODEM only",
X	"	(Z) = Option applies to ZMODEM only",
X	"Usage:	sz [-12+aBbdefkLlNnquvXy] [-] file ...",
X	"	sz [-1Beqv] -c COMMAND",
X	"	1 Use stdout for modem input",
X#ifdef CSTOPB
X	"	2 Use 2 stop bits",
X#endif
X	"	+ Append to existing destination file (Z)",
X	"	a (ASCII) change NL to CR/LF",
X	"	b Binary file transfer override",
X	"	c send COMMAND (Z)",
X	"	d Change '.' to '/' in pathnames (Y/Z)",
X	"	e Escape all control characters (Z)",
X	"	f send Full pathname (Y/Z)",
X	"	i send COMMAND, ack Immediately (Z)",
X	"	k Send 1024 byte packets (Y)",
X	"	L N Limit subpacket length to N bytes (Z)",
X	"	l N Limit frame length to N bytes (l>=L) (Z)",
X	"	n send file if source Newer or longer (Z)",
X	"	N send file if source different length or date (Z)",
X	"	o Use 16 bit CRC instead of 32 bit CRC (Z)",
X	"	p Protect existing destination file (Z)",
X	"	r Resume/Recover interrupted file transfer (Z)",
X	"	q Quiet (no progress reports)",
X	"	u Unlink file after transmission",
X	"	v Verbose - debugging information",
X	"	X XMODEM protocol - send no pathnames",
X	"	y Yes, overwrite existing file (Z)",
X	"- as pathname sends standard input as sPID.sz or environment ONAME",
X	""
X};
X
Xusage()
X{
X	char **pp;
X
X	for (pp=babble; **pp; ++pp)
X		fprintf(stderr, "%s\n", *pp);
X	fprintf(stderr, "%s for %s by Chuck Forsberg  ", VERSION, OS);
X	exit(1);
X}
X
X/*
X * Get the receiver's init parameters
X */
Xgetzrxinit()
X{
X	register n;
X	struct stat f;
X
X	for (n=10; --n>=0; ) {
X		
X		switch (zgethdr(Rxhdr, 1)) {
X		case ZCHALLENGE:	/* Echo receiver's challenge numbr */
X			stohdr(Rxpos);
X			zshhdr(ZACK, Txhdr);
X			continue;
X		case ZCOMMAND:		/* They didn't see out ZRQINIT */
X			stohdr(0L);
X			zshhdr(ZRQINIT, Txhdr);
X			continue;
X		case ZRINIT:
X			Rxflags = 0377 & Rxhdr[ZF0];
X			Txfcs32 = (Wantfcs32 && (Rxflags & CANFC32));
X			Rxbuflen = (0377 & Rxhdr[ZP0])+((0377 & Rxhdr[ZP1])<<8);
X			vfile("Rxbuflen=%d Tframlen=%d", Rxbuflen, Tframlen);
X			if ( !Fromcu)
X				signal(SIGINT, SIG_IGN);
X#ifdef USG
X			mode(2);	/* Set cbreak, XON/XOFF, etc. */
X#endif
X#ifndef READCHECK
X#ifndef USG
X			/* Use 1024 byte frames if no sample/interrupt */
X			if (Rxbuflen < 32 || Rxbuflen > 1024) {
X				Rxbuflen = 1024;
X				vfile("Rxbuflen=%d", Rxbuflen);
X			}
X#endif
X#endif
X			/* Override to force shorter frame length */
X			if (Rxbuflen && (Rxbuflen>Tframlen) && (Tframlen>=32))
X				Rxbuflen = Tframlen;
X			if ( !Rxbuflen && (Tframlen>=32) && (Tframlen<=1024))
X				Rxbuflen = Tframlen;
X			vfile("Rxbuflen=%d", Rxbuflen);
X
X			/* If using a pipe for testing set lower buf len */
X			fstat(iofd, &f);
X			if ((f.st_mode & S_IFMT) != S_IFCHR
X			  && (Rxbuflen == 0 || Rxbuflen > 4096))
X				Rxbuflen = 4096;
X			/*
X			 * If input is not a regular file, force ACK's each 1024
X			 *  (A smarter strategey could be used here ...)
X			 */
X			fstat(fileno(in), &f);
X			if (((f.st_mode & S_IFMT) != S_IFREG)
X			  && (Rxbuflen == 0 || Rxbuflen > 1024))
X				Rxbuflen = 1024;
X			vfile("Rxbuflen=%d", Rxbuflen);
X
X			return (sendzsinit());
X		case ZCAN:
X		case TIMEOUT:
X			return ERROR;
X		case ZRQINIT:
X			if (Rxhdr[ZF0] == ZCOMMAND)
X				continue;
X		default:
X			zshhdr(ZNAK, Txhdr);
X			continue;
X		}
X	}
X	return ERROR;
X}
X
X/* Send send-init information */
Xsendzsinit()
X{
X	register c;
X	register errors;
X
X	if (Myattn[0] == '\0')
X		return OK;
X	errors = 0;
X	for (;;) {
X		stohdr(0L);
X		zsbhdr(ZSINIT, Txhdr);
X		zsdata(Myattn, 1+strlen(Myattn), ZCRCW);
X		c = zgethdr(Rxhdr, 1);
X		switch (c) {
X		case ZCAN:
X			return ERROR;
X		case ZACK:
X			return OK;
X		default:
X			if (++errors > 9)
X				return ERROR;
X			continue;
X		}
X	}
X}
X
X/* Send file name and related info */
Xzsendfile(buf, blen)
Xchar *buf;
X{
X	register c;
X
X	for (;;) {
X		Txhdr[ZF0] = Lzconv;	/* file conversion request */
X		Txhdr[ZF1] = Lzmanag;	/* file management request */
X		Txhdr[ZF2] = Lztrans;	/* file transport request */
X		Txhdr[ZF3] = 0;
X		zsbhdr(ZFILE, Txhdr);
X		zsdata(buf, blen, ZCRCW);
Xagain:
X		c = zgethdr(Rxhdr, 1);
X		switch (c) {
X		case ZRINIT:
X			goto again;
X		case ZCAN:
X		case TIMEOUT:
X		case ZABORT:
X		case ZFIN:
X			return ERROR;
X		case ZSKIP:
X			fclose(in); return c;
X		case ZRPOS:
X			fseek(in, Rxpos, 0);
X			Txpos = Rxpos; Lastc = -1; Dontread = FALSE;
X			return zsendfdata();
X		default:
X			continue;
X		}
X	}
X}
X
X/* Send the data in the file */
Xzsendfdata()
X{
X	register c, e;
X	register newcnt;
X	register long tcount = 0;
X	static int tleft = 6;	/* Counter for test mode */
X	extern int setjmp();
X
X	if (Baudrate > 300)
X		blklen = 256;
X	if (Baudrate > 2400)
X		blklen = KSIZE;
X/*	if (Rxbuflen && blklen>Rxbuflen) */
X	if (Rxbuflen)
X		blklen = Rxbuflen;
X	if (blkopt && blklen > blkopt)
X		blklen = blkopt;
X	vfile("Rxbuflen=%d blklen=%d", Rxbuflen, blklen);
Xsomemore:
X	if (setjmp(intrjmp)) {
Xwaitack:
X		c = getinsync();
X		switch (c) {
X		default:
X		case ZCAN:
X			fclose(in);
X			return ERROR;
X		case ZSKIP:
X			fclose(in);
X			return c;
X		case ZACK:
X		case ZRPOS:
X			break;
X		case ZRINIT:
X			return OK;
X		}
X#ifdef READCHECK
X		/*
X		 * If the reverse channel can be tested for data,
X		 *  this logic may be used to detect error packets
X		 *  sent by the receiver, in place of setjmp/longjmp
X		 *  rdchk(fdes) returns non 0 if a character is available
X		 */
X		while (rdchk(iofd)) {
X#ifdef SVR2
X			switch (checked)
X#else
X			switch (readline(1))
X#endif
X			{
X			case CAN:
X			case ZPAD:
X				goto waitack;
X			case XOFF:		/* Wait a while for an XON */
X			case XOFF|0200:
X				readline(100);
X			}
X		}
X#endif
X	}
X
X	if ( !Fromcu)
X		signal(SIGINT, onintr);
X	newcnt = Rxbuflen;
X	stohdr(Txpos);
X	zsbhdr(ZDATA, Txhdr);
X
X	/*
X	 * Special testing mode.  This should force receiver to Attn,ZRPOS
X	 *  many times.  Each time the signal should be caught, causing the
X	 *  file to be started over from the beginning.
X	 */
X	if (Testattn) {
X		if ( --tleft)
X			while (tcount < 20000) {
X				printf(qbf); fflush(stdout);
X				tcount += strlen(qbf);
X#ifdef READCHECK
X				while (rdchk(iofd)) {
X#ifdef SVR2
X					switch (checked)
X#else
X					switch (readline(1))
X#endif
X					{
X					case CAN:
X					case ZPAD:
X#ifdef TCFLSH
X						ioctl(iofd, TCFLSH, 1);
X#endif
X						goto waitack;
X					case XOFF:	/* Wait for XON */
X					case XOFF|0200:
X						readline(100);
X					}
X				}
X#endif
X			}
X		signal(SIGINT, SIG_IGN); canit();
X		sleep(3); purgeline(); mode(0);
X		printf("\nsz: Tcount = %ld\n", tcount);
X		if (tleft) {
X			printf("ERROR: Interrupts Not Caught\n");
X			exit(1);
X		}
X		exit(0);
X	}
X
X	do {
X		if (Dontread) {
X			c = Lastc;
X		} else {
X			c = zfilbuf(txbuf, blklen);
X			Lastread = Txpos;  Lastc = c;
X		}
X		if (Verbose > 10)
X			vfile("Dontread=%d c=%d", Dontread, c);
X		Dontread = FALSE;
X		if (c < blklen)
X			e = ZCRCE;
X		else if (Rxbuflen && (newcnt -= c) <= 0)
X			e = ZCRCW;
X		else
X			e = ZCRCG;
X		zsdata(txbuf, c, e);
X		Txpos += c;
X		if (e == ZCRCW)
X			goto waitack;
X#ifdef READCHECK
X		/*
X		 * If the reverse channel can be tested for data,
X		 *  this logic may be used to detect error packets
X		 *  sent by the receiver, in place of setjmp/longjmp
X		 *  rdchk(fdes) returns non 0 if a character is available
X		 */
X		fflush(stdout);
X		while (rdchk(iofd)) {
X#ifdef SVR2
X			switch (checked)
X#else
X			switch (readline(1))
X#endif
X			{
X			case CAN:
X			case ZPAD:
X#ifdef TCFLSH
X				ioctl(iofd, TCFLSH, 1);
X#endif
X				/* zcrce - dinna wanna start a ping-pong game */
X				zsdata(txbuf, 0, ZCRCE);
X				goto waitack;
X			case XOFF:		/* Wait a while for an XON */
X			case XOFF|0200:
X				readline(100);
X			}
X		}
X#endif
X	} while (c == blklen);
X	if ( !Fromcu)
X		signal(SIGINT, SIG_IGN);
X
X	for (;;) {
X		stohdr(Txpos);
X		zsbhdr(ZEOF, Txhdr);
X		switch (getinsync()) {
X		case ZACK:
X			continue;
X		case ZRPOS:
X			goto somemore;
X		case ZRINIT:
X			return OK;
X		case ZSKIP:
X			fclose(in);
X			return c;
X		default:
X			fclose(in);
X			return ERROR;
X		}
X	}
X}
X
X/*
X * Respond to receiver's complaint, get back in sync with receiver
X */
Xgetinsync()
X{
X	register c;
X
X	for (;;) {
X		if (Testattn) {
X			printf("\r\n\n\n***** Signal Caught *****\r\n");
X			Rxpos = 0; c = ZRPOS;
X		} else
X			c = zgethdr(Rxhdr, 0);
X		switch (c) {
X		case ZCAN:
X		case ZABORT:
X		case ZFIN:
X		case TIMEOUT:
X			return ERROR;
X		case ZRPOS:
X			if (Lastc >= 0 && Lastread == Rxpos) {
X				Dontread = TRUE;
X			} else {
X				clearerr(in);	/* In case file EOF seen */
X				fseek(in, Rxpos, 0);
X			}
X			Txpos = Rxpos;
X			return c;
X		case ZACK:
X			return c;
X		case ZRINIT:
X		case ZSKIP:
X			fclose(in);
X			return c;
X		case ERROR:
X		default:
X			zsbhdr(ZNAK, Txhdr);
X			continue;
X		}
X	}
X}
X/* Say "bibi" to the receiver, try to do it cleanly */
Xsaybibi()
X{
X	for (;;) {
X		stohdr(0L);
X		zsbhdr(ZFIN, Txhdr);
X		switch (zgethdr(Rxhdr, 0)) {
X		case ZFIN:
X			sendline('O'); sendline('O'); flushmo();
X		case ZCAN:
X		case TIMEOUT:
X			return;
X		}
X	}
X}
X
X/* Local screen character display function */
Xbttyout(c)
X{
X	if (Verbose)
X		putc(c, stderr);
X}
X
X/* Send command and related info */
Xzsendcmd(buf, blen)
Xchar *buf;
X{
X	register c, errors;
X	long cmdnum;
X
X	cmdnum = getpid();
X	errors = 0;
X	for (;;) {
X		stohdr(cmdnum);
X		Txhdr[ZF0] = Cmdack1;
X		zsbhdr(ZCOMMAND, Txhdr);
X		zsdata(buf, blen, ZCRCW);
Xlisten:
X		Rxtimeout = 100;		/* Ten second wait for resp. */
X		c = zgethdr(Rxhdr, 1);
X
X		switch (c) {
X		case ZRINIT:
X			continue;
X		case ERROR:
X		case TIMEOUT:
X			if (++errors > Cmdtries)
X				return ERROR;
X			continue;
X		case ZCAN:
X		case ZABORT:
X		case ZFIN:
X		case ZSKIP:
X		case ZRPOS:
X			return ERROR;
X		default:
X			if (++errors > 10)
X				return ERROR;
X			continue;
X		case ZCOMPL:
X			Exitcode = Rxpos;
X			saybibi();
X			return OK;
X		case ZRQINIT:
X			vfile("******** RZ *******");
X			system("rz");
X			vfile("******** SZ *******");
X			goto listen;
X		}
X	}
X}
X
X/*
X * If called as sb use YMODEM protocol
X */
Xchkinvok(s)
Xchar *s;
X{
X	register char *p;
X
X	p = s;
X	while (*p == '-')
X		s = ++p;
X	while (*p)
X		if (*p++ == '/')
X			s = p;
X	if (*s == 'v') {
X		Verbose=1; ++s;
X	}
X	Progname = s;
X	if (s[0]=='s' && s[1]=='b') {
X		Nozmodem = TRUE; blklen=KSIZE;
X	}
X}
X
SHAR_EOF
chmod 0600 sz.c || echo "restore of sz.c fails"
sed 's/^X//' << 'SHAR_EOF' > zm.c &&
X/*
X *   Z M . C
X *    ZMODEM protocol primitives
X *    01-19-87  Chuck Forsberg Omen Technology Inc
X *
X * Entry point Functions:
X *	zsbhdr(type, hdr) send binary header
X *	zshhdr(type, hdr) send hex header
X *	zgethdr(hdr, eflag) receive header - binary or hex
X *	zsdata(buf, len, frameend) send data
X *	zrdata(buf, len) receive data
X *	stohdr(pos) store position data in Txhdr
X *	long rclhdr(hdr) recover position offset from header
X */
X
X#ifndef CANFDX
X#include "zmodem.h"
Xint Rxtimeout = 100;		/* Tenths of seconds to wait for something */
X#endif
X
Xstatic char *frametypes[] = {
X	"Carrier Lost",		/* -3 */
X	"TIMEOUT",		/* -2 */
X	"ERROR",		/* -1 */
X#define FTOFFSET 3
X	"ZRQINIT",
X	"ZRINIT",
X	"ZSINIT",
X	"ZACK",
X	"ZFILE",
X	"ZSKIP",
X	"ZNAK",
X	"ZABORT",
X	"ZFIN",
X	"ZRPOS",
X	"ZDATA",
X	"ZEOF",
X	"ZFERR",
X	"ZCRC",
X	"ZCHALLENGE",
X	"ZCOMPL",
X	"ZCAN",
X	"ZFREECNT",
X	"ZCOMMAND",
X	"ZSTDERR",
X	"xxxxx"
X#define FRTYPES 22	/* Total number of frame types in this array */
X			/*  not including psuedo negative entries */
X};
X
X/* Send ZMODEM binary header hdr of type type */
Xzsbhdr(type, hdr)
Xregister char *hdr;
X{
X	register n;
X	register unsigned short crc;
X
X	vfile("zsbhdr: %s %lx", frametypes[type+FTOFFSET], rclhdr(hdr));
X	if (type == ZDATA)
X		for (n = Znulls; --n >=0; )
X			zsendline(0);
X
X	xsendline(ZPAD); xsendline(ZDLE);
X
X	if (Txfcs32)
X		zsbh32(hdr, type);
X	else {
X		xsendline(ZBIN); zsendline(type); crc = updcrc(type, 0);
X
X		for (n=4; --n >= 0;) {
X			zsendline(*hdr);
X			crc = updcrc((0377& *hdr++), crc);
X		}
X		crc = updcrc(0,updcrc(0,crc));
X		zsendline(crc>>8);
X		zsendline(crc);
X	}
X	if (type != ZDATA)
X		flushmo();
X}
X
X
X/* Send ZMODEM binary header hdr of type type */
Xzsbh32(hdr, type)
Xregister char *hdr;
X{
X	register n;
X	register unsigned long crc;
X
X	xsendline(ZBIN32);  zsendline(type);
X	crc = 0xFFFFFFFF; crc = UPDC32(type, crc);
X
X	for (n=4; --n >= 0;) {
X		zsendline(*hdr);
X		crc = UPDC32((0377& *hdr++), crc);
X	}
X	crc = ~crc;
X	for (n=4; --n >= 0;) {
X		zsendline(crc);  crc >>= 8;
X	}
X}
X
X/* Send ZMODEM HEX header hdr of type type */
Xzshhdr(type, hdr)
Xregister char *hdr;
X{
X	register n;
X	register unsigned short crc;
X
X	vfile("zshhdr: %s %lx", frametypes[type+FTOFFSET], rclhdr(hdr));
X	sendline(ZPAD); sendline(ZPAD); sendline(ZDLE); sendline(ZHEX);
X	zputhex(type);
X
X	crc = updcrc(type, 0);
X	for (n=4; --n >= 0;) {
X		zputhex(*hdr); crc = updcrc((0377& *hdr++), crc);
X	}
X	crc = updcrc(0,updcrc(0,crc));
X	zputhex(crc>>8); zputhex(crc);
X
X	/* Make it printable on remote machine */
X	sendline(015); sendline(012);
X	/*
X	 * Uncork the remote in case a fake XOFF has stopped data flow
X	 */
X	if (type != ZFIN)
X		sendline(021);
X	flushmo();
X}
X
X/*
X * Send binary array buf of length length, with ending ZDLE sequence frameend
X */
Xzsdata(buf, length, frameend)
Xregister char *buf;
X{
X	register unsigned short crc;
X
X	vfile("zsdata: length=%d end=%x", length, frameend);
X	if (Txfcs32)
X		zsda32(buf, length, frameend);
X	else {
SHAR_EOF
echo "End of part 4, continue with part 5"
echo "5" > s2_seq_.tmp
exit 0