#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 3 (of 4)."
# Contents:  rz.c zm.c
# Wrapped by hankin@sauron on Tue Dec  5 10:22:27 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'rz.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'rz.c'\"
else
echo shar: Extracting \"'rz.c'\" \(32265 characters\)
sed "s/^X//" >'rz.c' <<'END_OF_FILE'
X#define VERSION "3.00 4-19-89"
X#define PUBDIR "/usr/spool/uucppublic"
X
X/*% cc -compat -M2 -Ox -K -i -DMD % -o rz; size rz;
X<-xtx-*> cc386 -Ox -DMD rz.c -o $B/rz;  size $B/rz
X *
X * rz.c By Chuck Forsberg
X *
X *	cc -O rz.c -o rz		USG (3.0) Unix
X * 	cc -O -DV7  rz.c -o rz		Unix V7, BSD 2.8 - 4.3
X *
X *	ln rz rb;  ln rz rx			For either system
X *
X *	ln rz /usr/bin/rzrmail		For remote mail.  Make this the
X *					login shell. rzrmail then calls
X *					rmail(1) to deliver mail.
X *
X * To compile on VMS:
X *
X *	define LNK$LIBRARY   SYS$LIBRARY:VAXCRTL.OLB
X *	cc rz.c
X *	cc vvmodem.c
X *	link rz,vvmodem
X *	rz :== $disk:[username.subdir]rz.exe
X *      For high speed, try increasing the SYSGEN parameter TTY_TYPAHDSZ to 256.
X *
X *
X *  Unix is a trademark of Western Electric Company
X *
X * A program for Unix to receive files and commands from computers running
X *  Professional-YAM, PowerCom, YAM, IMP, or programs supporting XMODEM.
X *  rz uses Unix buffered input to reduce wasted CPU time.
X *
X *	This version implements ZMODEM Run Length Encoding 
X *	and variable length headers.  These features were not funded
X *	by the original Telenet development contract.  This software,
X *	including these features, may be freely used for non
X *	commercial and educational purposes.  This software may also
X *	be freely used to support file transfer operations to or from
X *	licensed Omen Technology products.  Contact Omen Technology
X *	for licensing for other uses.  Any programs which use part or
X *	all of this software must be provided in source form with this
X *	notice intact except by written permission from Omen
X *	Technology Incorporated.
X *
X *		Omen Technology Inc		FAX: 503-621-3745
X *		Post Office Box 4681
X *		Portland OR 97208
X *
X *	Previous versions of this program (not containing the extensions
X *	listed above) remain in the public domain.
X *
X *	This code is made available in the hope it will be useful,
X *	BUT WITHOUT ANY WARRANTY OF ANY KIND OR LIABILITY FOR ANY
X *	DAMAGES OF ANY KIND.
X *
X *
X * Iff the program is invoked by rzCOMMAND, output is piped to 
X * "COMMAND filename"  (Unix only)
X *
X *  Some systems (Venix, Coherent, Regulus) may not support tty raw mode
X *  read(2) the same way as Unix. ONEREAD must be defined to force one
X *  character reads for these systems. Added 7-01-84 CAF
X *
X *  Alarm signal handling changed to work with 4.2 BSD 7-15-84 CAF 
X *
X *  BIX added 6-30-87 to support BIX(TM) upload protocol used by the
X *  Byte Information Exchange.
X *
X *  NFGVMIN Updated 2-18-87 CAF for Xenix systems where c_cc[VMIN]
X *  doesn't work properly (even though it compiles without error!),
X *
X *  SEGMENTS=n added 2-21-88 as a model for CP/M programs
X *    for CP/M-80 systems that cannot overlap modem and disk I/O.
X *
X *  VMS flavor hacks begin with rz version 2.00
X *
X *  -DMD may be added to compiler command line to compile in
X *    Directory-creating routines from Public Domain TAR by John Gilmore
X *
X *  HOWMANY may be tuned for best performance
X *
X *  USG UNIX (3.0) ioctl conventions courtesy  Jeff Martin
X */
X
X#ifdef vax11c
X#include <types.h>
X#include <stat.h>
X#define LOGFILE "rzlog.tmp"
X#include <stdio.h>
X#include <signal.h>
X#include <setjmp.h>
X#include <ctype.h>
X#include <errno.h>
X#define OS "VMS"
X#define BUFREAD
Xextern int errno;
X#define SS_NORMAL SS$_NORMAL
X
X#ifndef PROGNAME
X#define PROGNAME "rz"
X#endif
X
X
X#else
X
X
X#define SS_NORMAL 0
X#define LOGFILE "/tmp/rzlog"
X#include <stdio.h>
X#include <signal.h>
X#include <setjmp.h>
X#include <ctype.h>
X#include <errno.h>
Xextern int errno;
XFILE *popen();
X#endif
X
X#define OK 0
X#define FALSE 0
X#define TRUE 1
X#define ERROR (-1)
X
X/*
X * Max value for HOWMANY is 255.
X *   A larger value reduces system overhead but may evoke kernel bugs.
X *   133 corresponds to an XMODEM/CRC sector
X */
X#ifndef HOWMANY
X#define HOWMANY 133
X#endif
X
X/* Ward Christensen / CP/M parameters - Don't change these! */
X#define ENQ 005
X#define CAN ('X'&037)
X#define XOFF ('s'&037)
X#define XON ('q'&037)
X#define SOH 1
X#define STX 2
X#define EOT 4
X#define ACK 6
X#define NAK 025
X#define CPMEOF 032
X#define WANTCRC 0103	/* send C not NAK to get crc not checksum */
X#define TIMEOUT (-2)
X#define RCDO (-3)
X#define GCOUNT (-4)
X#define ERRORMAX 5
X#define RETRYMAX 5
X#define WCEOT (-10)
X#define PATHLEN 257	/* ready for 4.2 bsd ? */
X#define UNIXFILE 0xF000	/* The S_IFMT file mask bit for stat */
X
Xint Zmodem=0;		/* ZMODEM protocol requested */
Xint Nozmodem = 0;	/* If invoked as "rb" */
Xunsigned Baudrate = 2400;
Xunsigned Effbaud = 2400;
X#ifdef vax11c
X#include "vrzsz.c"	/* most of the system dependent stuff here */
X#else
X#include "rbsb.c"	/* most of the system dependent stuff here */
X#endif
X#include "crctab.c"
X
Xchar *substr();
XFILE *fout;
X
X/*
X * Routine to calculate the free bytes on the current file system
X *  ~0 means many free bytes (unknown)
X */
Xlong getfree()
X{
X	return(~0L);	/* many free bytes ... */
X}
X
Xint Lastrx;
Xint Crcflg;
Xint Firstsec;
Xint Eofseen;		/* indicates cpm eof (^Z) has been received */
Xint errors;
Xint Restricted=0;	/* restricted; no /.. or ../ in filenames */
X#ifdef ONEREAD
X/* Sorry, Regulus and some others don't work right in raw mode! */
Xint Readnum = 1;	/* Number of bytes to ask for in read() from modem */
X#else
Xint Readnum = HOWMANY;	/* Number of bytes to ask for in read() from modem */
X#endif
X
X#define DEFBYTL 2000000000L	/* default rx file size */
Xlong Bytesleft;		/* number of bytes of incoming file left */
Xlong Modtime;		/* Unix style mod time for incoming file */
Xint Filemode;		/* Unix style mode for incoming file */
Xchar Pathname[PATHLEN];
Xchar *Progname;		/* the name by which we were called */
X
Xint Batch=0;
Xint Topipe=0;
Xint MakeLCPathname=TRUE;	/* make received pathname lower case */
Xint Verbose=0;
Xint Quiet=0;		/* overrides logic that would otherwise set verbose */
Xint Nflag = 0;		/* Don't really transfer files */
Xint Rxclob=FALSE;	/* Clobber existing file */
Xint Rxbinary=FALSE;	/* receive all files in bin mode */
Xint Rxascii=FALSE;	/* receive files in ascii (translate) mode */
Xint Thisbinary;		/* current file is to be received in bin mode */
Xint Blklen;		/* record length of received packets */
X
X#ifdef SEGMENTS
Xint chinseg = 0;	/* Number of characters received in this data seg */
Xchar secbuf[1+(SEGMENTS+1)*1024];
X#else
Xchar secbuf[1025];
X#endif
X
X
Xchar linbuf[HOWMANY];
Xint Lleft=0;		/* number of characters in linbuf */
Xtime_t timep[2];
Xchar Lzmanag;		/* Local file management request */
Xchar zconv;		/* ZMODEM file conversion request */
Xchar zmanag;		/* ZMODEM file management request */
Xchar ztrans;		/* ZMODEM file transport request */
Xint Zctlesc;		/* Encode control characters */
Xint Zrwindow = 1400;	/* RX window size (controls garbage count) */
X
Xjmp_buf tohere;		/* For the interrupt on RX timeout */
X
X#define xsendline(c) sendline(c)
X
X#include "zm.c"
X
X#include "zmr.c"
X
Xint tryzhdrtype=ZRINIT;	/* Header type to send corresponding to
Last rx close */
X
Xalrm()
X{
X	longjmp(tohere, -1);
X}
X
X/* called by signal interrupt or terminate to clean things up */
Xbibi(n)
X{
X	if (Zmodem)
X		zmputs(Attn);
X	canit(); mode(0);
X	fprintf(stderr, "rz: caught signal %d; exiting", n);
X	cucheck();
X	exit(128+n);
X}
X
Xmain(argc, argv)
Xchar *argv[];
X{
X	register char *cp;
X	register npats;
X	char *virgin, **patts;
X	char *getenv();
X	int exitcode;
X
X	Rxtimeout = 100;
X	setbuf(stderr, NULL);
X	if ((cp=getenv("SHELL")) && (substr(cp, "rsh") || substr(cp, "rksh")))
X		Restricted=TRUE;
X
X	from_cu();
X#ifdef vax11c
X	chkinvok(virgin = PROGNAME);
X#else
X	chkinvok(virgin=argv[0]);	/* if called as [-]rzCOMMAND set flag */
X#endif
X	npats = 0;
X	while (--argc) {
X		cp = *++argv;
X		if (*cp == '-') {
X			while( *++cp) {
X				switch(*cp) {
X				case '\\':
X					 cp[1] = toupper(cp[1]);  continue;
X				case '+':
X					Lzmanag = ZMAPND; break;
X				case 'a':
X					Rxascii=TRUE;  break;
X				case 'b':
X					Rxbinary=TRUE; break;
X				case 'c':
X					Crcflg=TRUE; break;
X#ifndef vax11c
X				case 'D':
X					Nflag = TRUE; break;
X#endif
X				case 'e':
X					Zctlesc = 1; break;
X				case 'p':
X					Lzmanag = ZMPROT;  break;
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 'w':
X					if (--argc < 1) {
X						usage();
X					}
X					Zrwindow = atoi(*++argv);
X					break;
X				case 'u':
X					MakeLCPathname=FALSE; break;
X				case 'v':
X					++Verbose; break;
X				case 'y':
X					Rxclob=TRUE; 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			}
X		}
X	}
X	if (npats > 1)
X		usage();
X	if (Batch && npats)
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		fprintf(stderr, "argv[0]=%s Progname=%s\n", virgin, Progname);
X	}
X	if (Fromcu && !Quiet) {
X		if (Verbose == 0)
X			Verbose = 2;
X	}
X	vfile("%s %s for %s\n", Progname, VERSION, OS);
X	mode(1);
X	if (signal(SIGINT, bibi) == SIG_IGN) {
X		signal(SIGINT, SIG_IGN); signal(SIGKILL, SIG_IGN);
X	}
X	else {
X		signal(SIGINT, bibi); signal(SIGKILL, bibi);
X	}
X	signal(SIGTERM, bibi);
X	if (wcreceive(npats, patts)==ERROR) {
X		exitcode=0200;
X		canit();
X	}
X	mode(0);
X	if (exitcode && !Zmodem)	/* bellow again with all thy might. */
X		canit();
X	if (exitcode)
X		cucheck();
X	exit(exitcode ? exitcode:SS_NORMAL);
X}
X
X
Xusage()
X{
X	cucheck();
X	fprintf(stderr,"Usage:	rz [-abeuvy]		(ZMODEM)\n");
X	fprintf(stderr,"or	rb [-abuvy]		(YMODEM)\n");
X	fprintf(stderr,"or	rx [-abcv] file	(XMODEM or XMODEM-1k)\n");
X	fprintf(stderr,"	  -a ASCII transfer (strip CR)\n");
X	fprintf(stderr,"	  -b Binary transfer for all files\n");
X#ifndef vax11c
X	fprintf(stderr,"	  -c Use 16 bit CRC	(XMODEM)\n");
X#endif
X	fprintf(stderr,"	  -e Escape control characters	(ZMODEM)\n");
X	fprintf(stderr,"	  -v Verbose more v's give more info\n");
X	fprintf(stderr,"	  -y Yes, clobber existing file if any\n");
X	fprintf(stderr,"%s %s for %s by Chuck Forsberg, Omen Technology INC\n",
X	  Progname, VERSION, OS);
X	fprintf(stderr, "\t\t\042The High Reliability Software\042\n");
X	exit(SS_NORMAL);
X}
X/*
X *  Debugging information output interface routine
X */
X/* VARARGS1 */
Xvfile(f, a, b, c)
Xregister char *f;
X{
X	if (Verbose > 2) {
X		fprintf(stderr, f, a, b, c);
X		fprintf(stderr, "\n");
X	}
X}
X
X/*
X * Let's receive something already.
X */
X
Xchar *rbmsg =
X"%s ready. To begin transfer, type \"%s file ...\" to your modem program\r\n\n";
X
Xwcreceive(argc, argp)
Xchar **argp;
X{
X	register c;
X
X	if (Batch || argc==0) {
X		Crcflg=1;
X		if ( !Quiet)
X			fprintf(stderr, rbmsg, Progname, Nozmodem?"sb":"sz");
X		if (c=tryz()) {
X			if (c == ZCOMPL)
X				return OK;
X			if (c == ERROR)
X				goto fubar;
X			c = rzfiles();
X			if (c)
X				goto fubar;
X		} else {
X			for (;;) {
X				if (wcrxpn(secbuf)== ERROR)
X					goto fubar;
X				if (secbuf[0]==0)
X					return OK;
X				if (procheader(secbuf) == ERROR)
X					goto fubar;
X				if (wcrx()==ERROR)
X					goto fubar;
X			}
X		}
X	} else {
X		Bytesleft = DEFBYTL; Filemode = 0; Modtime = 0L;
X
X		procheader(""); strcpy(Pathname, *argp); checkpath(Pathname);
X		fprintf(stderr, "\nrz: ready to receive %s\r\n", Pathname);
X		if ((fout=fopen(Pathname, "w")) == NULL)
X			return ERROR;
X		if (wcrx()==ERROR)
X			goto fubar;
X	}
X	return OK;
Xfubar:
X	canit();
X#ifndef vax11c
X	if (Topipe && fout) {
X		pclose(fout);  return ERROR;
X	}
X#endif
X	Modtime = 1;
X	if (fout)
X		fclose(fout);
X#ifndef vax11c
X	if (Restricted) {
X		unlink(Pathname);
X		fprintf(stderr, "\r\nrz: %s removed.\r\n", Pathname);
X	}
X#endif
X	return ERROR;
X}
X
X
X/*
X * Fetch a pathname from the other end as a C ctyle ASCIZ string.
X * Length is indeterminate as long as less than Blklen
X * A null string represents no more files (YMODEM)
X */
Xwcrxpn(rpn)
Xchar *rpn;	/* receive a pathname */
X{
X	register c;
X
X#ifdef NFGVMIN
X	readline(1);
X#else
X	purgeline();
X#endif
X
Xet_tu:
X	Firstsec=TRUE;  Eofseen=FALSE;
X	sendline(Crcflg?WANTCRC:NAK);
X	Lleft=0;	/* Do read next time ... */
X	while ((c = wcgetsec(rpn, 100)) != 0) {
X		if (c == WCEOT) {
X			zperr( "Pathname fetch returned %d", c);
X			sendline(ACK);
X			Lleft=0;	/* Do read next time ... */
X			readline(1);
X			goto et_tu;
X		}
X		return ERROR;
X	}
X	sendline(ACK);
X	return OK;
X}
X
X/*
X * Adapted from CMODEM13.C, written by
X * Jack M. Wierda and Roderick W. Hart
X */
X
Xwcrx()
X{
X	register int sectnum, sectcurr;
X	register char sendchar;
X	register char *p;
X	int cblklen;			/* bytes to dump this block */
X
X	Firstsec=TRUE;sectnum=0; Eofseen=FALSE;
X	sendchar=Crcflg?WANTCRC:NAK;
X
X	for (;;) {
X		sendline(sendchar);	/* send it now, we're ready! */
X		Lleft=0;	/* Do read next time ... */
X		sectcurr=wcgetsec(secbuf, (sectnum&0177)?50:130);
X		report(sectcurr);
X		if (sectcurr==(sectnum+1 &0377)) {
X			sectnum++;
X			cblklen = Bytesleft>Blklen ? Blklen:Bytesleft;
X			if (putsec(secbuf, cblklen)==ERROR)
X				return ERROR;
X			if ((Bytesleft-=cblklen) < 0)
X				Bytesleft = 0;
X			sendchar=ACK;
X		}
X		else if (sectcurr==(sectnum&0377)) {
X			zperr( "Received dup Sector");
X			sendchar=ACK;
X		}
X		else if (sectcurr==WCEOT) {
X			if (closeit())
X				return ERROR;
X			sendline(ACK);
X			Lleft=0;	/* Do read next time ... */
X			return OK;
X		}
X		else if (sectcurr==ERROR)
X			return ERROR;
X		else {
X			zperr( "Sync Error");
X			return ERROR;
X		}
X	}
X}
X
X/*
X * Wcgetsec fetches a Ward Christensen type sector.
X * Returns sector number encountered or ERROR if valid sector not received,
X * or CAN CAN received
X * or WCEOT if eot sector
X * time is timeout for first char, set to 4 seconds thereafter
X ***************** NO ACK IS SENT IF SECTOR IS RECEIVED OK **************
X *    (Caller must do that when he is good and ready to get next sector)
X */
X
Xwcgetsec(rxbuf, maxtime)
Xchar *rxbuf;
Xint maxtime;
X{
X	register checksum, wcj, firstch;
X	register unsigned short oldcrc;
X	register char *p;
X	int sectcurr;
X
X	for (Lastrx=errors=0; errors<RETRYMAX; errors++) {
X
X		if ((firstch=readline(maxtime))==STX) {
X			Blklen=1024; goto get2;
X		}
X		if (firstch==SOH) {
X			Blklen=128;
Xget2:
X			sectcurr=readline(1);
X			if ((sectcurr+(oldcrc=readline(1)))==0377) {
X				oldcrc=checksum=0;
X				for (p=rxbuf,wcj=Blklen; --wcj>=0; ) {
X					if ((firstch=readline(1)) < 0)
X						goto bilge;
X					oldcrc=updcrc(firstch, oldcrc);
X					checksum += (*p++ = firstch);
X				}
X				if ((firstch=readline(1)) < 0)
X					goto bilge;
X				if (Crcflg) {
X					oldcrc=updcrc(firstch, oldcrc);
X					if ((firstch=readline(1)) < 0)
X						goto bilge;
X					oldcrc=updcrc(firstch, oldcrc);
X					if (oldcrc & 0xFFFF)
X						zperr( "CRC");
X					else {
X						Firstsec=FALSE;
X						return sectcurr;
X					}
X				}
X				else if (((checksum-firstch)&0377)==0) {
X					Firstsec=FALSE;
X					return sectcurr;
X				}
X				else
X					zperr( "Checksum");
X			}
X			else
X				zperr("Sector number garbled");
X		}
X		/* make sure eot really is eot and not just mixmash */
X#ifdef NFGVMIN
X		else if (firstch==EOT && readline(1)==TIMEOUT)
X			return WCEOT;
X#else
X		else if (firstch==EOT && Lleft==0)
X			return WCEOT;
X#endif
X		else if (firstch==CAN) {
X			if (Lastrx==CAN) {
X				zperr( "Sender CANcelled");
X				return ERROR;
X			} else {
X				Lastrx=CAN;
X				continue;
X			}
X		}
X		else if (firstch==TIMEOUT) {
X			if (Firstsec)
X				goto humbug;
Xbilge:
X			zperr( "TIMEOUT");
X		}
X		else
X			zperr( "Got 0%o sector header", firstch);
X
Xhumbug:
X		Lastrx=0;
X		while(readline(1)!=TIMEOUT)
X			;
X		if (Firstsec) {
X			sendline(Crcflg?WANTCRC:NAK);
X			Lleft=0;	/* Do read next time ... */
X		} else {
X			maxtime=40; sendline(NAK);
X			Lleft=0;	/* Do read next time ... */
X		}
X	}
X	/* try to stop the bubble machine. */
X	canit();
X	return ERROR;
X}
X
X#ifndef vax11c
X/*
X * This version of readline is reasoably well suited for
X * reading many characters.
X *  (except, currently, for the Regulus version!)
X *
X * timeout is in tenths of seconds
X */
Xreadline(timeout)
Xint timeout;
X{
X	register n;
X	static char *cdq;	/* pointer for removing chars from linbuf */
X
X	if (--Lleft >= 0) {
X		if (Verbose > 8) {
X			fprintf(stderr, "%02x ", *cdq&0377);
X		}
X		return (*cdq++ & 0377);
X	}
X	n = timeout/10;
X	if (n < 2)
X		n = 3;
X	if (Verbose > 5)
X		fprintf(stderr, "Calling read: alarm=%d  Readnum=%d ",
X		  n, Readnum);
X	if (setjmp(tohere)) {
X#ifdef TIOCFLUSH
X/*		ioctl(0, TIOCFLUSH, 0); */
X#endif
X		Lleft = 0;
X		if (Verbose>1)
X			fprintf(stderr, "Readline:TIMEOUT\n");
X		return TIMEOUT;
X	}
X	signal(SIGALRM, alrm); alarm(n);
X	Lleft=read(0, cdq=linbuf, Readnum);
X	alarm(0);
X	if (Verbose > 5) {
X		fprintf(stderr, "Read returned %d bytes\n", Lleft);
X	}
X	if (Lleft < 1)
X		return TIMEOUT;
X	--Lleft;
X	if (Verbose > 8) {
X		fprintf(stderr, "%02x ", *cdq&0377);
X	}
X	return (*cdq++ & 0377);
X}
X
X
X
X/*
X * Purge the modem input queue of all characters
X */
Xpurgeline()
X{
X	Lleft = 0;
X#ifdef USG
X	ioctl(0, TCFLSH, 0);
X#else
X	lseek(0, 0L, 2);
X#endif
X}
X#endif
X
X
X/*
X * Process incoming file information header
X */
Xprocheader(name)
Xchar *name;
X{
X	register char *openmode, *p, **pp;
X
X	/* set default parameters and overrides */
X	openmode = "w";
X	Thisbinary = (!Rxascii) || Rxbinary;
X	if (Lzmanag)
X		zmanag = Lzmanag;
X
X	/*
X	 *  Process ZMODEM remote file management requests
X	 */
X	if (!Rxbinary && zconv == ZCNL)	/* Remote ASCII override */
X		Thisbinary = 0;
X	if (zconv == ZCBIN)	/* Remote Binary override */
X		Thisbinary = TRUE;
X	else if (zmanag == ZMAPND)
X		openmode = "a";
X
X#ifndef BIX
X	/* Check for existing file */
X	if (!Rxclob && (zmanag&ZMMASK) != ZMCLOB && (fout=fopen(name, "r"))) {
X		fclose(fout);  return ERROR;
X	}
X#endif
X
X	Bytesleft = DEFBYTL; Filemode = 0; Modtime = 0L;
X
X	p = name + 1 + strlen(name);
X	if (*p) {	/* file coming from Unix or DOS system */
X		sscanf(p, "%ld%lo%o", &Bytesleft, &Modtime, &Filemode);
X#ifndef vax11c
X		if (Filemode & UNIXFILE)
X			++Thisbinary;
X#endif
X		if (Verbose) {
X			fprintf(stderr,  "Incoming: %s %ld %lo %o\n",
X			  name, Bytesleft, Modtime, Filemode);
X		}
X	}
X
X#ifdef BIX
X	if ((fout=fopen("scratchpad", openmode)) == NULL)
X		return ERROR;
X	return OK;
X#else
X
X	else {		/* File coming from CP/M system */
X		for (p=name; *p; ++p)		/* change / to _ */
X			if ( *p == '/')
X				*p = '_';
X
X		if ( *--p == '.')		/* zap trailing period */
X			*p = 0;
X	}
X
X#ifndef vax11c
X	if (!Zmodem && MakeLCPathname && !IsAnyLower(name)
X	  && !(Filemode&UNIXFILE))
X		uncaps(name);
X#endif
X	if (Topipe > 0) {
X		sprintf(Pathname, "%s %s", Progname+2, name);
X		if (Verbose)
X			fprintf(stderr,  "Topipe: %s %s\n",
X			  Pathname, Thisbinary?"BIN":"ASCII");
X#ifndef vax11c
X		if ((fout=popen(Pathname, "w")) == NULL)
X			return ERROR;
X#endif
X	} else {
X		strcpy(Pathname, name);
X		if (Verbose) {
X			fprintf(stderr,  "Receiving %s %s %s\n",
X			  name, Thisbinary?"BIN":"ASCII", openmode);
X		}
X		checkpath(name);
X		if (Nflag)
X			name = "/dev/null";
X#ifndef vax11c
X		if (name[0] == '!' || name[0] == '|') {
X			if ( !(fout = popen(name+1, "w"))) {
X				return ERROR;
X			}
X			Topipe = -1;  return(OK);
X		}
X#endif
X#ifdef MD
X		fout = fopen(name, openmode);
X		if ( !fout)
X			if (make_dirs(name))
X				fout = fopen(name, openmode);
X#else
X		fout = fopen(name, openmode);
X#endif
X		if ( !fout)
X			return ERROR;
X	}
X	return OK;
X#endif /* BIX */
X}
X
X#ifdef MD
X/*
X *  Directory-creating routines from Public Domain TAR by John Gilmore
X */
X
X/*
X * After a file/link/symlink/dir creation has failed, see if
X * it's because some required directory was not present, and if
X * so, create all required dirs.
X */
Xmake_dirs(pathname)
Xregister char *pathname;
X{
X	register char *p;		/* Points into path */
X	int madeone = 0;		/* Did we do anything yet? */
X	int save_errno = errno;		/* Remember caller's errno */
X	char *strchr();
X
X	if (errno != ENOENT)
X		return 0;		/* Not our problem */
X
X	for (p = strchr(pathname, '/'); p != NULL; p = strchr(p+1, '/')) {
X		/* Avoid mkdir of empty string, if leading or double '/' */
X		if (p == pathname || p[-1] == '/')
X			continue;
X		/* Avoid mkdir where last part of path is '.' */
X		if (p[-1] == '.' && (p == pathname+1 || p[-2] == '/'))
X			continue;
X		*p = 0;				/* Truncate the path there */
X		if ( !mkdir(pathname, 0777)) {	/* Try to create it as a dir */
X			vfile("Made directory %s\n", pathname);
X			madeone++;		/* Remember if we made one */
X			*p = '/';
X			continue;
X		}
X		*p = '/';
X		if (errno == EEXIST)		/* Directory already exists */
X			continue;
X		/*
X		 * Some other error in the mkdir.  We return to the caller.
X		 */
X		break;
X	}
X	errno = save_errno;		/* Restore caller's errno */
X	return madeone;			/* Tell them to retry if we made one */
X}
X
X#if (MD != 2)
X#define	TERM_SIGNAL(status)	((status) & 0x7F)
X#define TERM_COREDUMP(status)	(((status) & 0x80) != 0)
X#define TERM_VALUE(status)	((status) >> 8)
X/*
X * Make a directory.  Compatible with the mkdir() system call on 4.2BSD.
X */
Xmkdir(dpath, dmode)
Xchar *dpath;
Xint dmode;
X{
X	int cpid, status;
X	struct stat statbuf;
X
X	if (stat(dpath,&statbuf) == 0) {
X		errno = EEXIST;		/* Stat worked, so it already exists */
X		return -1;
X	}
X
X	/* If stat fails for a reason other than non-existence, return error */
X	if (errno != ENOENT) return -1; 
X
X	switch (cpid = fork()) {
X
X	case -1:			/* Error in fork() */
X		return(-1);		/* Errno is set already */
X
X	case 0:				/* Child process */
X		/*
X		 * Cheap hack to set mode of new directory.  Since this
X		 * child process is going away anyway, we zap its umask.
X		 * FIXME, this won't suffice to set SUID, SGID, etc. on this
X		 * directory.  Does anybody care?
X		 */
X		status = umask(0);	/* Get current umask */
X		status = umask(status | (0777 & ~dmode)); /* Set for mkdir */
X		execl("/bin/mkdir", "mkdir", dpath, (char *)0);
X		_exit(-1);		/* Can't exec /bin/mkdir */
X	
X	default:			/* Parent process */
X		while (cpid != wait(&status)) ;	/* Wait for kid to finish */
X	}
X
X	if (TERM_SIGNAL(status) != 0 || TERM_VALUE(status) != 0) {
X		errno = EIO;		/* We don't know why, but */
X		return -1;		/* /bin/mkdir failed */
X	}
X
X	return 0;
X}
X#endif /* MD != 2 */
X#endif /* MD */
X
X/*
X * Putsec writes the n characters of buf to receive file fout.
X *  If not in binary mode, carriage returns, and all characters
X *  starting with CPMEOF are discarded.
X */
Xputsec(buf, n)
Xchar *buf;
Xregister n;
X{
X	register char *p;
X
X	if (n == 0)
X		return OK;
X	if (Thisbinary) {
X		for (p=buf; --n>=0; )
X			putc( *p++, fout);
X	}
X	else {
X		if (Eofseen)
X			return OK;
X		for (p=buf; --n>=0; ++p ) {
X			if ( *p == '\r')
X				continue;
X			if (*p == CPMEOF) {
X				Eofseen=TRUE; return OK;
X			}
X			putc(*p ,fout);
X		}
X	}
X	return OK;
X}
X
X#ifndef vax11c
X/*
X *  Send a character to modem.  Small is beautiful.
X */
Xsendline(c)
X{
X	char d;
X
X	d = c;
X	if (Verbose>6)
X		fprintf(stderr, "Sendline: %x\n", c);
X	write(1, &d, 1);
X}
X
Xflushmo() {}
X#endif
X
X
X
X
X
X/* make string s lower case */
Xuncaps(s)
Xregister char *s;
X{
X	for ( ; *s; ++s)
X		if (isupper(*s))
X			*s = tolower(*s);
X}
X/*
X * IsAnyLower returns TRUE if string s has lower case letters.
X */
XIsAnyLower(s)
Xregister char *s;
X{
X	for ( ; *s; ++s)
X		if (islower(*s))
X			return TRUE;
X	return FALSE;
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
X/*
X * Log an error
X */
X/*VARARGS1*/
Xzperr(s,p,u)
Xchar *s, *p, *u;
X{
X	if (Verbose <= 0)
X		return;
X	fprintf(stderr, "Retry %d: ", errors);
X	fprintf(stderr, s, p, u);
X	fprintf(stderr, "\n");
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#ifdef vax11c
X	raw_wbuf(strlen(canistr), canistr);
X	purgeline();
X#else
X	printf(canistr);
X	Lleft=0;	/* Do read next time ... */
X	fflush(stdout);
X#endif
X}
X
X
Xreport(sct)
Xint sct;
X{
X	if (Verbose>1)
X		fprintf(stderr,"%03d%c",sct,sct%10? ' ' : '\r');
X}
X
X/*
X * If called as [-][dir/../]vrzCOMMAND set Verbose to 1
X * If called as [-][dir/../]rzCOMMAND set the pipe flag
X * If called as rb 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]=='r' && s[1]=='z')
X		Batch = TRUE;
X	if (s[0]=='r' && s[1]=='b')
X		Batch = Nozmodem = TRUE;
X	if (s[2] && s[0]=='r' && s[1]=='b')
X		Topipe = 1;
X	if (s[2] && s[0]=='r' && s[1]=='z')
X		Topipe = 1;
X}
X
X/*
X * Totalitarian Communist pathname processing
X */
Xcheckpath(name)
Xchar *name;
X{
X	if (Restricted) {
X		if (fopen(name, "r") != NULL) {
X			canit();
X			fprintf(stderr, "\r\nrz: %s exists\n", name);
X			bibi(-1);
X		}
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\nrz:\tSecurity Violation\r\n");
X			bibi(-1);
X		}
X	}
X}
X
X/*
X * Initialize for Zmodem receive attempt, try to activate Zmodem sender
X *  Handles ZSINIT frame
X *  Return ZFILE if Zmodem filename received, -1 on error,
X *   ZCOMPL if transaction finished,  else 0
X */
Xtryz()
X{
X	register c, n;
X	register cmdzack1flg;
X
X	if (Nozmodem)		/* Check for "rb" program name */
X		return 0;
X
X
X	for (n=Zmodem?15:5; --n>=0; ) {
X		/* Set buffer length (0) and capability flags */
X#ifdef SEGMENTS
X		stohdr(SEGMENTS*1024L);
X#else
X		stohdr(0L);
X#endif
X#ifdef CANBREAK
X		Txhdr[ZF0] = CANFC32|CANFDX|CANOVIO|CANBRK;
X#else
X		Txhdr[ZF0] = CANFC32|CANFDX|CANOVIO;
X#endif
X		if (Zctlesc)
X			Txhdr[ZF0] |= TESCCTL;
X		Txhdr[ZF0] |= CANRLE;
X		Txhdr[ZF1] = CANVHDR;
X		/* tryzhdrtype may == ZRINIT */
X		zshhdr(4,tryzhdrtype, Txhdr);
X		if (tryzhdrtype == ZSKIP)	/* Don't skip too far */
X			tryzhdrtype = ZRINIT;	/* CAF 8-21-87 */
Xagain:
X		switch (zgethdr(Rxhdr, 0)) {
X		case ZRQINIT:
X			if (Rxhdr[ZF3] & 0x80)
X				Usevhdrs = 1;	/* we can var header */
X			continue;
X		case ZEOF:
X			continue;
X		case TIMEOUT:
X			continue;
X		case ZFILE:
X			zconv = Rxhdr[ZF0];
X			zmanag = Rxhdr[ZF1];
X			ztrans = Rxhdr[ZF2];
X			if (Rxhdr[ZF3] & ZCANVHDR)
X				Usevhdrs = TRUE;
X			tryzhdrtype = ZRINIT;
X			c = zrdata(secbuf, 1024);
X			mode(3);
X			if (c == GOTCRCW)
X				return ZFILE;
X			zshhdr(4,ZNAK, Txhdr);
X			goto again;
X		case ZSINIT:
X			Zctlesc = TESCCTL & Rxhdr[ZF0];
X			if (zrdata(Attn, ZATTNLEN) == GOTCRCW) {
X				stohdr(1L);
X				zshhdr(4,ZACK, Txhdr);
X				goto again;
X			}
X			zshhdr(4,ZNAK, Txhdr);
X			goto again;
X		case ZFREECNT:
X			stohdr(getfree());
X			zshhdr(4,ZACK, Txhdr);
X			goto again;
X		case ZCOMMAND:
X#ifdef vax11c
X			return ERROR;
X#else
X			cmdzack1flg = Rxhdr[ZF0];
X			if (zrdata(secbuf, 1024) == GOTCRCW) {
X				if (cmdzack1flg & ZCACK1)
X					stohdr(0L);
X				else
X					stohdr((long)sys2(secbuf));
X				purgeline();	/* dump impatient questions */
X				do {
X					zshhdr(4,ZCOMPL, Txhdr);
X				}
X				while (++errors<20 && zgethdr(Rxhdr,1) != ZFIN);
X				ackbibi();
X				if (cmdzack1flg & ZCACK1)
X					exec2(secbuf);
X				return ZCOMPL;
X			}
X			zshhdr(4,ZNAK, Txhdr); goto again;
X#endif
X		case ZCOMPL:
X			goto again;
X		default:
X			continue;
X		case ZFIN:
X			ackbibi(); return ZCOMPL;
X		case ZCAN:
X			return ERROR;
X		}
X	}
X	return 0;
X}
X
X/*
X * Receive 1 or more files with ZMODEM protocol
X */
Xrzfiles()
X{
X	register c;
X
X	for (;;) {
X		switch (c = rzfile()) {
X		case ZEOF:
X		case ZSKIP:
X			switch (tryz()) {
X			case ZCOMPL:
X				return OK;
X			default:
X				return ERROR;
X			case ZFILE:
X				break;
X			}
X			continue;
X		default:
X			return c;
X		case ERROR:
X			return ERROR;
X		}
X	}
X}
X
X/*
X * Receive a file with ZMODEM protocol
X *  Assumes file name frame is in secbuf
X */
Xrzfile()
X{
X	register c, n;
X	long rxbytes;
X
X	Eofseen=FALSE;
X	if (procheader(secbuf) == ERROR) {
X		return (tryzhdrtype = ZSKIP);
X	}
X
X	n = 20; rxbytes = 0l;
X
X	for (;;) {
X#ifdef SEGMENTS
X		chinseg = 0;
X#endif
X		stohdr(rxbytes);
X		zshhdr(4,ZRPOS, Txhdr);
Xnxthdr:
X		switch (c = zgethdr(Rxhdr, 0)) {
X		default:
X			vfile("rzfile: zgethdr returned %d", c);
X			return ERROR;
X		case ZNAK:
X		case TIMEOUT:
X#ifdef SEGMENTS
X			putsec(secbuf, chinseg);
X			chinseg = 0;
X#endif
X			if ( --n < 0) {
X				vfile("rzfile: zgethdr returned %d", c);
X				return ERROR;
X			}
X		case ZFILE:
X			zrdata(secbuf, 1024);
X			continue;
X		case ZEOF:
X#ifdef SEGMENTS
X			putsec(secbuf, chinseg);
X			chinseg = 0;
X#endif
X			if (rclhdr(Rxhdr) != rxbytes) {
X				/*
X				 * Ignore eof if it's at wrong place - force
X				 *  a timeout because the eof might have gone
X				 *  out before we sent our zrpos.
X				 */
X				errors = 0;  goto nxthdr;
X			}
X			if (closeit()) {
X				tryzhdrtype = ZFERR;
X				vfile("rzfile: closeit returned <> 0");
X				return ERROR;
X			}
X			vfile("rzfile: normal EOF");
X			return c;
X		case ERROR:	/* Too much garbage in header search error */
X#ifdef SEGMENTS
X			putsec(secbuf, chinseg);
X			chinseg = 0;
X#endif
X			if ( --n < 0) {
X				vfile("rzfile: zgethdr returned %d", c);
X				return ERROR;
X			}
X			zmputs(Attn);
X			continue;
X		case ZSKIP:
X#ifdef SEGMENTS
X			putsec(secbuf, chinseg);
X			chinseg = 0;
X#endif
X			Modtime = 1;
X			closeit();
X			vfile("rzfile: Sender SKIPPED file");
X			return c;
X		case ZDATA:
X			if (rclhdr(Rxhdr) != rxbytes) {
X				if ( --n < 0) {
X					return ERROR;
X				}
X#ifdef SEGMENTS
X				putsec(secbuf, chinseg);
X				chinseg = 0;
X#endif
X				zmputs(Attn);  continue;
X			}
Xmoredata:
X			if (Verbose>1)
X				fprintf(stderr, "\r%7ld ZMODEM%s    ",
X				  rxbytes, Crc32r?" CRC-32":"");
X#ifdef SEGMENTS
X			if (chinseg >= (1024 * SEGMENTS)) {
X				putsec(secbuf, chinseg);
X				chinseg = 0;
X			}
X			switch (c = zrdata(secbuf+chinseg, 1024))
X#else
X			switch (c = zrdata(secbuf, 1024))
X#endif
X			{
X			case ZCAN:
X#ifdef SEGMENTS
X				putsec(secbuf, chinseg);
X				chinseg = 0;
X#endif
X				vfile("rzfile: zgethdr returned %d", c);
X				return ERROR;
X			case ERROR:	/* CRC error */
X#ifdef SEGMENTS
X				putsec(secbuf, chinseg);
X				chinseg = 0;
X#endif
X				if ( --n < 0) {
X					vfile("rzfile: zgethdr returned %d", c);
X					return ERROR;
X				}
X				zmputs(Attn);
X				continue;
X			case TIMEOUT:
X#ifdef SEGMENTS
X				putsec(secbuf, chinseg);
X				chinseg = 0;
X#endif
X				if ( --n < 0) {
X					vfile("rzfile: zgethdr returned %d", c);
X					return ERROR;
X				}
X				continue;
X			case GOTCRCW:
X				n = 20;
X#ifdef SEGMENTS
X				chinseg += Rxcount;
X				putsec(secbuf, chinseg);
X				chinseg = 0;
X#else
X				putsec(secbuf, Rxcount);
X#endif
X				rxbytes += Rxcount;
X				stohdr(rxbytes);
X				zshhdr(4,ZACK, Txhdr);
X				sendline(XON);
X				goto nxthdr;
X			case GOTCRCQ:
X				n = 20;
X#ifdef SEGMENTS
X				chinseg += Rxcount;
X#else
X				putsec(secbuf, Rxcount);
X#endif
X				rxbytes += Rxcount;
X				stohdr(rxbytes);
X				zshhdr(4,ZACK, Txhdr);
X				goto moredata;
X			case GOTCRCG:
X				n = 20;
X#ifdef SEGMENTS
X				chinseg += Rxcount;
X#else
X				putsec(secbuf, Rxcount);
X#endif
X				rxbytes += Rxcount;
X				goto moredata;
X			case GOTCRCE:
X				n = 20;
X#ifdef SEGMENTS
X				chinseg += Rxcount;
X#else
X				putsec(secbuf, Rxcount);
X#endif
X				rxbytes += Rxcount;
X				goto nxthdr;
X			}
X		}
X	}
X}
X
X/*
X * Send a string to the modem, processing for \336 (sleep 1 sec)
X *   and \335 (break signal)
X */
Xzmputs(s)
Xchar *s;
X{
X	register c;
X
X	while (*s) {
X		switch (c = *s++) {
X		case '\336':
X			sleep(1); continue;
X		case '\335':
X			sendbrk(); continue;
X		default:
X			sendline(c);
X		}
X	}
X}
X
X/*
X * Close the receive dataset, return OK or ERROR
X */
Xcloseit()
X{
X	time_t time();
X
X#ifndef vax11c
X	if (Topipe) {
X		if (pclose(fout)) {
X			return ERROR;
X		}
X		return OK;
X	}
X#endif
X	if (fclose(fout)==ERROR) {
X		fprintf(stderr, "file close ERROR\n");
X		return ERROR;
X	}
X#ifndef vax11c
X	if (Modtime) {
X		timep[0] = time(NULL);
X		timep[1] = Modtime;
X		utime(Pathname, timep);
X	}
X#endif
X	if ((Filemode&S_IFMT) == S_IFREG)
X		chmod(Pathname, (07777 & Filemode));
X	return OK;
X}
X
X/*
X * Ack a ZFIN packet, let byegones be byegones
X */
Xackbibi()
X{
X	register n;
X
X	vfile("ackbibi:");
X	Readnum = 1;
X	stohdr(0L);
X	for (n=3; --n>=0; ) {
X		purgeline();
X		zshhdr(4,ZFIN, Txhdr);
X		switch (readline(100)) {
X		case 'O':
X			readline(1);	/* Discard 2nd 'O' */
X			vfile("ackbibi complete");
X			return;
X		case RCDO:
X			return;
X		case TIMEOUT:
X		default:
X			break;
X		}
X	}
X}
X
X
X
X/*
X * Local console output simulation
X */
Xbttyout(c)
X{
X	if (Verbose || Fromcu)
X		putc(c, stderr);
X}
X
X#ifndef vax11c
X/*
X * Strip leading ! if present, do shell escape. 
X */
Xsys2(s)
Xregister char *s;
X{
X	if (*s == '!')
X		++s;
X	return system(s);
X}
X/*
X * Strip leading ! if present, do exec.
X */
Xexec2(s)
Xregister char *s;
X{
X	if (*s == '!')
X		++s;
X	mode(0);
X	execl("/bin/sh", "sh", "-c", s);
X}
X#endif
X/* End of rz.c */
END_OF_FILE
if test 32265 -ne `wc -c <'rz.c'`; then
    echo shar: \"'rz.c'\" unpacked with wrong size!
fi
# end of 'rz.c'
fi
if test -f 'zm.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'zm.c'\"
else
echo shar: Extracting \"'zm.c'\" \(17884 characters\)
sed "s/^X//" >'zm.c' <<'END_OF_FILE'
X/*
X *   Z M . C
X *    ZMODEM protocol primitives
X *    04-17-89  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 *	This version implements ZMODEM Run Length Encoding, Comparision,
X *	and variable length headers.  These features were not funded
X *	by the original Telenet development contract.  This software,
X *	including these features, may be freely used for non
X *	commercial and educational purposes.  This software may also
X *	be freely used to support file transfer operations to or from
X *	licensed Omen Technology products.  Contact Omen Technology
X *	for licensing for other uses.  Any programs which use part or
X *	all of this software must be provided in source form with this
X *	notice intact except by written permission from Omen
X *	Technology Incorporated.
X *
X *		Omen Technology Inc		FAX: 503-621-3745
X *		Post Office Box 4681
X *		Portland OR 97208
X *
X *	Previous versions of this program (not containing the extensions
X *	listed above) remain in the public domain.
X *
X *	This code is made available in the hope it will be useful,
X *	BUT WITHOUT ANY WARRANTY OF ANY KIND OR LIABILITY FOR ANY
X *	DAMAGES OF ANY KIND.
X *
X */
X
X#ifndef CANFDX
X#include "zmodem.h"
Xint Rxtimeout = 100;		/* Tenths of seconds to wait for something */
X#endif
X
X#ifndef UNSL
X#define UNSL
X#endif
X
X
X/* Globals used by ZMODEM functions */
Xint Rxframeind;		/* ZBIN ZBIN32, or ZHEX type of frame */
Xint Rxtype;		/* Type of header received */
Xint Rxhlen;		/* Length of header received */
Xint Rxcount;		/* Count of data bytes received */
Xchar Rxhdr[ZMAXHLEN];	/* Received header */
Xchar Txhdr[ZMAXHLEN];	/* Transmitted header */
Xlong Rxpos;		/* Received file position */
Xlong Txpos;		/* Transmitted file position */
Xint Txfcs32;		/* TURE means send binary frames with 32 bit FCS */
Xint Crc32t;		/* Controls 32 bit CRC being sent */
X			/* 1 == CRC32,  2 == CRC32 + RLE */
Xint Crc32r;		/* Indicates/controls 32 bit CRC being received */
X			/* 0 == CRC16,  1 == CRC32,  2 == CRC32 + RLE */
Xint Usevhdrs;		/* Use variable length headers */
Xint Znulls;		/* Number of nulls to send at beginning of ZDATA hdr */
Xchar Attn[ZATTNLEN+1];	/* Attention string rx sends to tx on err */
Xchar *Altcan;		/* Alternate canit string */
X
Xstatic lastsent;	/* Last char we sent */
Xstatic Not8bit;		/* Seven bits seen on header */
X
Xstatic char *frametypes[] = {
X	"No Response to Error Correction Request",	/* -4 */
X	"No Carrier Detect",		/* -3 */
X	"TIMEOUT",		/* -2 */
X	"ERROR",		/* -1 */
X#define FTOFFSET 4
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
Xstatic char badcrc[] = "Bad CRC";
X
X/* Send ZMODEM binary header hdr of type type */
Xzsbhdr(len, type, hdr)
Xregister char *hdr;
X{
X	register int n;
X	register unsigned short crc;
X
X#ifndef DSZ
X	vfile("zsbhdr: %c %d %s %lx", Usevhdrs?'v':'f', len,
X	  frametypes[type+FTOFFSET], rclhdr(hdr));
X#endif
X	if (type == ZDATA)
X		for (n = Znulls; --n >=0; )
X			xsendline(0);
X
X	xsendline(ZPAD); xsendline(ZDLE);
X
X	switch (Crc32t=Txfcs32) {
X	case 2:
X		zsbh32(len, hdr, type, Usevhdrs?ZVBINR32:ZBINR32);
X		flushmo();  break;
X	case 1:
X		zsbh32(len, hdr, type, Usevhdrs?ZVBIN32:ZBIN32);  break;
X	default:
X		if (Usevhdrs) {
X			xsendline(ZVBIN);
X			zsendline(len);
X		}
X		else
X			xsendline(ZBIN);
X		zsendline(type);
X		crc = updcrc(type, 0);
X
X		for (n=len; --n >= 0; ++hdr) {
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(len, hdr, type, flavour)
Xregister char *hdr;
X{
X	register int n;
X	register UNSL long crc;
X
X	xsendline(flavour); 
X	if (Usevhdrs) 
X		zsendline(len);
X	zsendline(type);
X	crc = 0xFFFFFFFFL; crc = UPDC32(type, crc);
X
X	for (n=len; --n >= 0; ++hdr) {
X		crc = UPDC32((0377 & *hdr), crc);
X		zsendline(*hdr);
X	}
X	crc = ~crc;
X	for (n=4; --n >= 0;) {
X		zsendline((int)crc);
X		crc >>= 8;
X	}
X}
X
X/* Send ZMODEM HEX header hdr of type type */
Xzshhdr(len, type, hdr)
Xregister char *hdr;
X{
X	register int n;
X	register unsigned short crc;
X
X#ifndef DSZ
X	vfile("zshhdr: %c %d %s %lx", Usevhdrs?'v':'f', len,
X	  frametypes[type+FTOFFSET], rclhdr(hdr));
X#endif
X	sendline(ZPAD); sendline(ZPAD); sendline(ZDLE);
X	if (Usevhdrs) {
X		sendline(ZVHEX);
X		zputhex(len);
X	}
X	else
X		sendline(ZHEX);
X	zputhex(type);
X	Crc32t = 0;
X
X	crc = updcrc(type, 0);
X	for (n=len; --n >= 0; ++hdr) {
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(0212);
X	/*
X	 * Uncork the remote in case a fake XOFF has stopped data flow
X	 */
X	if (type != ZFIN && type != ZACK)
X		sendline(021);
X	flushmo();
X}
X
X/*
X * Send binary array buf of length length, with ending ZDLE sequence frameend
X */
Xstatic char *Zendnames[] = { "ZCRCE", "ZCRCG", "ZCRCQ", "ZCRCW"};
Xzsdata(buf, length, frameend)
Xregister char *buf;
X{
X	register unsigned short crc;
X
X#ifndef DSZ
X	vfile("zsdata: %d %s", length, Zendnames[frameend-ZCRCE&3]);
X#endif
X	switch (Crc32t) {
X	case 1:
X		zsda32(buf, length, frameend);  break;
X	case 2:
X		zsdar32(buf, length, frameend);  break;
X	default:
X		crc = 0;
X		for (;--length >= 0; ++buf) {
X			zsendline(*buf); crc = updcrc((0377 & *buf), crc);
X		}
X		xsendline(ZDLE); xsendline(frameend);
X		crc = updcrc(frameend, crc);
X
X		crc = updcrc(0,updcrc(0,crc));
X		zsendline(crc>>8); zsendline(crc);
X	}
X	if (frameend == ZCRCW) {
X		xsendline(XON);  flushmo();
X	}
X}
X
Xzsda32(buf, length, frameend)
Xregister char *buf;
X{
X	register int c;
X	register UNSL long crc;
X
X	crc = 0xFFFFFFFFL;
X	for (;--length >= 0; ++buf) {
X		c = *buf & 0377;
X		if (c & 0140)
X			xsendline(lastsent = c);
X		else
X			zsendline(c);
X		crc = UPDC32(c, crc);
X	}
X	xsendline(ZDLE); xsendline(frameend);
X	crc = UPDC32(frameend, crc);
X
X	crc = ~crc;
X	for (c=4; --c >= 0;) {
X		zsendline((int)crc);  crc >>= 8;
X	}
X}
X
X/*
X * Receive array buf of max length with ending ZDLE sequence
X *  and CRC.  Returns the ending character or error code.
X *  NB: On errors may store length+1 bytes!
X */
Xzrdata(buf, length)
Xregister char *buf;
X{
X	register int c;
X	register unsigned short crc;
X	register char *end;
X	register int d;
X
X	switch (Crc32r) {
X	case 1:
X		return zrdat32(buf, length);
X	case 2:
X		return zrdatr32(buf, length);
X	}
X
X	crc = Rxcount = 0;  end = buf + length;
X	while (buf <= end) {
X		if ((c = zdlread()) & ~0377) {
Xcrcfoo:
X			switch (c) {
X			case GOTCRCE:
X			case GOTCRCG:
X			case GOTCRCQ:
X			case GOTCRCW:
X				crc = updcrc((d=c)&0377, crc);
X				if ((c = zdlread()) & ~0377)
X					goto crcfoo;
X				crc = updcrc(c, crc);
X				if ((c = zdlread()) & ~0377)
X					goto crcfoo;
X				crc = updcrc(c, crc);
X				if (crc & 0xFFFF) {
X					zperr(badcrc);
X					return ERROR;
X				}
X				Rxcount = length - (end - buf);
X#ifndef DSZ
X				vfile("zrdata: %d  %s", Rxcount,
X				 Zendnames[d-GOTCRCE&3]);
X#endif
X				return d;
X			case GOTCAN:
X				zperr("Sender Canceled");
X				return ZCAN;
X			case TIMEOUT:
X				zperr("TIMEOUT");
X				return c;
X			default:
X				garbitch(); return c;
X			}
X		}
X		*buf++ = c;
X		crc = updcrc(c, crc);
X	}
X#ifdef DSZ
X	garbitch(); 
X#else
X	zperr("Data subpacket too long");
X#endif
X	return ERROR;
X}
X
Xzrdat32(buf, length)
Xregister char *buf;
X{
X	register int c;
X	register UNSL long crc;
X	register char *end;
X	register int d;
X
X	crc = 0xFFFFFFFFL;  Rxcount = 0;  end = buf + length;
X	while (buf <= end) {
X		if ((c = zdlread()) & ~0377) {
Xcrcfoo:
X			switch (c) {
X			case GOTCRCE:
X			case GOTCRCG:
X			case GOTCRCQ:
X			case GOTCRCW:
X				d = c;  c &= 0377;
X				crc = UPDC32(c, crc);
X				if ((c = zdlread()) & ~0377)
X					goto crcfoo;
X				crc = UPDC32(c, crc);
X				if ((c = zdlread()) & ~0377)
X					goto crcfoo;
X				crc = UPDC32(c, crc);
X				if ((c = zdlread()) & ~0377)
X					goto crcfoo;
X				crc = UPDC32(c, crc);
X				if ((c = zdlread()) & ~0377)
X					goto crcfoo;
X				crc = UPDC32(c, crc);
X				if (crc != 0xDEBB20E3) {
X					zperr(badcrc);
X					return ERROR;
X				}
X				Rxcount = length - (end - buf);
X#ifndef DSZ
X				vfile("zrdat32: %d %s", Rxcount,
X				 Zendnames[d-GOTCRCE&3]);
X#endif
X				return d;
X			case GOTCAN:
X				zperr("Sender Canceled");
X				return ZCAN;
X			case TIMEOUT:
X				zperr("TIMEOUT");
X				return c;
X			default:
X				garbitch(); return c;
X			}
X		}
X		*buf++ = c;
X		crc = UPDC32(c, crc);
X	}
X	zperr("Data subpacket too long");
X	return ERROR;
X}
X
Xgarbitch()
X{
X	zperr("Garbled data subpacket");
X}
X
X/*
X * Read a ZMODEM header to hdr, either binary or hex.
X *  eflag controls local display of non zmodem characters:
X *	0:  no display
X *	1:  display printing characters only
X *	2:  display all non ZMODEM characters
X *  On success, set Zmodem to 1, set Rxpos and return type of header.
X *   Otherwise return negative on error.
X *   Return ERROR instantly if ZCRCW sequence, for fast error recovery.
X */
Xzgethdr(hdr, eflag)
Xchar *hdr;
X{
X	register int c, n, cancount;
X
X	n = Zrwindow + Effbaud;		/* Max bytes before start of frame */
X	Rxframeind = Rxtype = 0;
X
Xstartover:
X	cancount = 5;
Xagain:
X	/* Return immediate ERROR if ZCRCW sequence seen */
X	switch (c = readline(Rxtimeout)) {
X	case RCDO:
X	case TIMEOUT:
X		goto fifi;
X	case CAN:
Xgotcan:
X		if (--cancount <= 0) {
X			c = ZCAN; goto fifi;
X		}
X		switch (c = readline(1)) {
X		case TIMEOUT:
X			goto again;
X		case ZCRCW:
X			switch (readline(1)) {
X			case TIMEOUT:
X				c = ERROR; goto fifi;
X			case RCDO:
X				goto fifi;
X			default:
X				goto agn2;
X			}
X		case RCDO:
X			goto fifi;
X		default:
X			break;
X		case CAN:
X			if (--cancount <= 0) {
X				c = ZCAN; goto fifi;
X			}
X			goto again;
X		}
X	/* **** FALL THRU TO **** */
X	default:
Xagn2:
X		if ( --n == 0) {
X			c = GCOUNT;  goto fifi;
X		}
X		if (eflag && ((c &= 0177) & 0140))
X			bttyout(c);
X		else if (eflag > 1)
X			bttyout(c);
X#ifdef UNIX
X		fflush(stderr);
X#endif
X		goto startover;
X	case ZPAD|0200:		/* This is what we want. */
X		Not8bit = c;
X	case ZPAD:		/* This is what we want. */
X		break;
X	}
X	cancount = 5;
Xsplat:
X	switch (c = noxrd7()) {
X	case ZPAD:
X		goto splat;
X	case RCDO:
X	case TIMEOUT:
X		goto fifi;
X	default:
X		goto agn2;
X	case ZDLE:		/* This is what we want. */
X		break;
X	}
X
X
X	Rxhlen = 4;		/* Set default length */
X	Rxframeind = c = noxrd7();
X	switch (c) {
X	case ZVBIN32:
X		if ((Rxhlen = c = readline(Rxtimeout)) < 0)
X			goto fifi;
X		if (c > ZMAXHLEN)
X			goto agn2;
X		Crc32r = 1;  c = zrbhd32(hdr); break;
X	case ZBIN32:
X		if (Usevhdrs)
X			goto agn2;
X		Crc32r = 1;  c = zrbhd32(hdr); break;
X	case ZVBINR32:
X		if ((Rxhlen = c = readline(Rxtimeout)) < 0)
X			goto fifi;
X		if (c > ZMAXHLEN)
X			goto agn2;
X		Crc32r = 2;  c = zrbhd32(hdr); break;
X	case ZBINR32:
X		if (Usevhdrs)
X			goto agn2;
X		Crc32r = 2;  c = zrbhd32(hdr); break;
X	case RCDO:
X	case TIMEOUT:
X		goto fifi;
X	case ZVBIN:
X		if ((Rxhlen = c = readline(Rxtimeout)) < 0)
X			goto fifi;
X		if (c > ZMAXHLEN)
X			goto agn2;
X		Crc32r = 0;  c = zrbhdr(hdr); break;
X	case ZBIN:
X		if (Usevhdrs)
X			goto agn2;
X		Crc32r = 0;  c = zrbhdr(hdr); break;
X	case ZVHEX:
X		if ((Rxhlen = c = zgethex()) < 0)
X			goto fifi;
X		if (c > ZMAXHLEN)
X			goto agn2;
X		Crc32r = 0;  c = zrhhdr(hdr); break;
X	case ZHEX:
X		if (Usevhdrs)
X			goto agn2;
X		Crc32r = 0;  c = zrhhdr(hdr); break;
X	case CAN:
X		goto gotcan;
X	default:
X		goto agn2;
X	}
X	Rxpos = hdr[ZP3] & 0377;
X	Rxpos = (Rxpos<<8) + (hdr[ZP2] & 0377);
X	Rxpos = (Rxpos<<8) + (hdr[ZP1] & 0377);
X	Rxpos = (Rxpos<<8) + (hdr[ZP0] & 0377);
Xfifi:
X	switch (c) {
X	case GOTCAN:
X		c = ZCAN;
X	/* **** FALL THRU TO **** */
X	case ZNAK:
X	case ZCAN:
X	case ERROR:
X	case TIMEOUT:
X	case RCDO:
X	case GCOUNT:
X		zperr("Got %s", frametypes[c+FTOFFSET]);
X	/* **** FALL THRU TO **** */
X#ifndef DSZ
X	default:
X		if (c >= -4 && c <= FRTYPES)
X			vfile("zgethdr: %c %d %s %lx", Rxframeind, Rxhlen,
X			  frametypes[c+FTOFFSET], Rxpos);
X		else
X			vfile("zgethdr: %d %lx", c, Rxpos);
X#endif
X	}
X	/* Use variable length headers if we got one */
X	if (c >= 0 && c <= FRTYPES && Rxframeind & 040)
X		Usevhdrs = 1;
X	return c;
X}
X
X/* Receive a binary style header (type and position) */
Xzrbhdr(hdr)
Xregister char *hdr;
X{
X	register int c, n;
X	register unsigned short crc;
X
X	if ((c = zdlread()) & ~0377)
X		return c;
X	Rxtype = c;
X	crc = updcrc(c, 0);
X
X	for (n=Rxhlen; --n >= 0; ++hdr) {
X		if ((c = zdlread()) & ~0377)
X			return c;
X		crc = updcrc(c, crc);
X		*hdr = c;
X	}
X	if ((c = zdlread()) & ~0377)
X		return c;
X	crc = updcrc(c, crc);
X	if ((c = zdlread()) & ~0377)
X		return c;
X	crc = updcrc(c, crc);
X	if (crc & 0xFFFF) {
X		zperr(badcrc);
X		return ERROR;
X	}
X#ifdef ZMODEM
X	Protocol = ZMODEM;
X#endif
X	Zmodem = 1;
X	return Rxtype;
X}
X
X/* Receive a binary style header (type and position) with 32 bit FCS */
Xzrbhd32(hdr)
Xregister char *hdr;
X{
X	register int c, n;
X	register UNSL long crc;
X
X	if ((c = zdlread()) & ~0377)
X		return c;
X	Rxtype = c;
X	crc = 0xFFFFFFFFL; crc = UPDC32(c, crc);
X#ifdef DEBUGZ
X	vfile("zrbhd32 c=%X  crc=%lX", c, crc);
X#endif
X
X	for (n=Rxhlen; --n >= 0; ++hdr) {
X		if ((c = zdlread()) & ~0377)
X			return c;
X		crc = UPDC32(c, crc);
X		*hdr = c;
X#ifdef DEBUGZ
X		vfile("zrbhd32 c=%X  crc=%lX", c, crc);
X#endif
X	}
X	for (n=4; --n >= 0;) {
X		if ((c = zdlread()) & ~0377)
X			return c;
X		crc = UPDC32(c, crc);
X#ifdef DEBUGZ
X		vfile("zrbhd32 c=%X  crc=%lX", c, crc);
X#endif
X	}
X	if (crc != 0xDEBB20E3) {
X		zperr(badcrc);
X		return ERROR;
X	}
X#ifdef ZMODEM
X	Protocol = ZMODEM;
X#endif
X	Zmodem = 1;
X	return Rxtype;
X}
X
X
X/* Receive a hex style header (type and position) */
Xzrhhdr(hdr)
Xchar *hdr;
X{
X	register int c;
X	register unsigned short crc;
X	register int n;
X
X	if ((c = zgethex()) < 0)
X		return c;
X	Rxtype = c;
X	crc = updcrc(c, 0);
X
X	for (n=Rxhlen; --n >= 0; ++hdr) {
X		if ((c = zgethex()) < 0)
X			return c;
X		crc = updcrc(c, crc);
X		*hdr = c;
X	}
X	if ((c = zgethex()) < 0)
X		return c;
X	crc = updcrc(c, crc);
X	if ((c = zgethex()) < 0)
X		return c;
X	crc = updcrc(c, crc);
X	if (crc & 0xFFFF) {
X		zperr(badcrc); return ERROR;
X	}
X	switch ( c = readline(1)) {
X	case 0215:
X		Not8bit = c;
X		/* **** FALL THRU TO **** */
X	case 015:
X	 	/* Throw away possible cr/lf */
X		switch (c = readline(1)) {
X		case 012:
X			Not8bit |= c;
X		}
X	}
X#ifdef ZMODEM
X	Protocol = ZMODEM;
X#endif
X	Zmodem = 1; return Rxtype;
X}
X
X/* Send a byte as two hex digits */
Xzputhex(c)
Xregister int c;
X{
X	static char	digits[]	= "0123456789abcdef";
X
X#ifdef DEBUGZ
X	if (Verbose>8)
X		vfile("zputhex: %02X", c);
X#endif
X	sendline(digits[(c&0xF0)>>4]);
X	sendline(digits[(c)&0xF]);
X}
X
X/*
X * Send character c with ZMODEM escape sequence encoding.
X *  Escape XON, XOFF. Escape CR following @ (Telenet net escape)
X */
Xzsendline(c)
X{
X
X	/* Quick check for non control characters */
X	if (c & 0140)
X		xsendline(lastsent = c);
X	else {
X		switch (c &= 0377) {
X		case ZDLE:
X			xsendline(ZDLE);
X			xsendline (lastsent = (c ^= 0100));
X			break;
X		case 015:
X		case 0215:
X			if (!Zctlesc && (lastsent & 0177) != '@')
X				goto sendit;
X		/* **** FALL THRU TO **** */
X		case 020:
X		case 021:
X		case 023:
X		case 0220:
X		case 0221:
X		case 0223:
X			xsendline(ZDLE);
X			c ^= 0100;
X	sendit:
X			xsendline(lastsent = c);
X			break;
X		default:
X			if (Zctlesc && ! (c & 0140)) {
X				xsendline(ZDLE);
X				c ^= 0100;
X			}
X			xsendline(lastsent = c);
X		}
X	}
X}
X
X/* Decode two lower case hex digits into an 8 bit byte value */
Xzgethex()
X{
X	register int c;
X
X	c = zgeth1();
X#ifdef DEBUGZ
X	if (Verbose>8)
X		vfile("zgethex: %02X", c);
X#endif
X	return c;
X}
Xzgeth1()
X{
X	register int c, n;
X
X	if ((c = noxrd7()) < 0)
X		return c;
X	n = c - '0';
X	if (n > 9)
X		n -= ('a' - ':');
X	if (n & ~0xF)
X		return ERROR;
X	if ((c = noxrd7()) < 0)
X		return c;
X	c -= '0';
X	if (c > 9)
X		c -= ('a' - ':');
X	if (c & ~0xF)
X		return ERROR;
X	c += (n<<4);
X	return c;
X}
X
X/*
X * Read a byte, checking for ZMODEM escape encoding
X *  including CAN*5 which represents a quick abort
X */
Xzdlread()
X{
X	register int c;
X
Xagain:
X	/* Quick check for non control characters */
X	if ((c = readline(Rxtimeout)) & 0140)
X		return c;
X	switch (c) {
X	case ZDLE:
X		break;
X	case 023:
X	case 0223:
X	case 021:
X	case 0221:
X		goto again;
X	default:
X		if (Zctlesc && !(c & 0140)) {
X			goto again;
X		}
X		return c;
X	}
Xagain2:
X	if ((c = readline(Rxtimeout)) < 0)
X		return c;
X	if (c == CAN && (c = readline(Rxtimeout)) < 0)
X		return c;
X	if (c == CAN && (c = readline(Rxtimeout)) < 0)
X		return c;
X	if (c == CAN && (c = readline(Rxtimeout)) < 0)
X		return c;
X	switch (c) {
X	case CAN:
X		return GOTCAN;
X	case ZCRCE:
X	case ZCRCG:
X	case ZCRCQ:
X	case ZCRCW:
X		return (c | GOTOR);
X	case ZRUB0:
X		return 0177;
X	case ZRUB1:
X		return 0377;
X	case 023:
X	case 0223:
X	case 021:
X	case 0221:
X		goto again2;
X	default:
X		if (Zctlesc && ! (c & 0140)) {
X			goto again2;
X		}
X		if ((c & 0140) ==  0100)
X			return (c ^ 0100);
X		break;
X	}
X	if (Verbose>1)
X		zperr("Bad escape sequence %x", c);
X	return ERROR;
X}
X
X/*
X * Read a character from the modem line with timeout.
X *  Eat parity, XON and XOFF characters.
X */
Xnoxrd7()
X{
X	register int c;
X
X	for (;;) {
X		if ((c = readline(Rxtimeout)) < 0)
X			return c;
X		switch (c &= 0177) {
X		case XON:
X		case XOFF:
X			continue;
X		default:
X			if (Zctlesc && !(c & 0140))
X				continue;
X		case '\r':
X		case '\n':
X		case ZDLE:
X			return c;
X		}
X	}
X}
X
X/* Store long integer pos in Txhdr */
Xstohdr(pos)
Xlong pos;
X{
X	Txhdr[ZP0] = pos;
X	Txhdr[ZP1] = pos>>8;
X	Txhdr[ZP2] = pos>>16;
X	Txhdr[ZP3] = pos>>24;
X}
X
X/* Recover a long integer from a header */
Xlong
Xrclhdr(hdr)
Xregister char *hdr;
X{
X	register long l;
X
X	l = (hdr[ZP3] & 0377);
X	l = (l << 8) | (hdr[ZP2] & 0377);
X	l = (l << 8) | (hdr[ZP1] & 0377);
X	l = (l << 8) | (hdr[ZP0] & 0377);
X	return l;
X}
X
X/* End of zm.c */
END_OF_FILE
if test 17884 -ne `wc -c <'zm.c'`; then
    echo shar: \"'zm.c'\" unpacked with wrong size!
fi
# end of 'zm.c'
fi
echo shar: End of archive 3 \(of 4\).
cp /dev/null ark3isdone
MISSING=""
for I in 1 2 3 4 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 4 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0

