Metropoli BBS
VIEWER: sz2.c MODE: TEXT (ASCII)
#define VERSION "sz 1.26 03-10-87"

#define PUBDIR "/usr/spool/uucppublic"



/*% cc -O -K -i -DNFGVMIN -DREADCHECK sz.c -lx -o sz; size sz

 *  (above for SYS III/V Xenix)



 * sz2.c By Chuck Forsberg

 *

 *	cc -O sz?.c -o sz		USG (SYS III/V) Unix

 *	cc -O -DSVR2 sz.c -o sz		Sys V Release 2 with non-blocking input

 *					Define to allow reverse channel checking

 *	cc -O -DV7  sz.c -o sz		Unix Version 7, 2.8 - 4.3 BSD

 *

 *	cc -O -K -i -DNFGVMIN -DREADCHECK sz.c -lx -o sz	Xenix

 *

 *	ln sz sb			**** All versions ****

 *

 *

 *  ******* Some systems (Venix, Coherent, Regulus) do not *******

 *  ******* support tty raw mode read(2) identically to    *******

 *  ******* Unix. ONEREAD must be defined to force one     *******

 *  ******* character reads for these systems.		   *******

 *

 * A program for Unix to send files and commands to computers running

 *  Professional-YAM, PowerCom, YAM, IMP, or programs supporting Y/XMODEM.

 *

 *  Sz uses buffered I/O to greatly reduce CPU time compared to UMODEM.

 *

 *  USG UNIX (3.0) ioctl conventions courtesy Jeff Martin

 */





extern char *substr(), *getenv();



#define LOGFILE "/tmp/szlog"

#define zperr vfile



#include <stdio.h>

#include <signal.h>

#include <setjmp.h>

#include <ctype.h>



#define PATHLEN 256

#define OK 0

#define FALSE 0

#define TRUE 1

#define ERROR (-1)



#define HOWMANY 2

extern int Zmodem;		/* ZMODEM protocol requested */

extern unsigned Baudrate;

extern int Fromcu;		/* Were called from cu or yam */

#include "bb.c"	/* most of the system dependent stuff here */

#include "zmodem2.h"

#define sendline(c) putchar(c & Wcsmask)

extern int iofd, Twostop, Nozmodem;

int onintr();

extern char *Progname;



/*

 * Attention string to be executed by receiver to interrupt streaming data

 *  when an error is detected.  A pause (0336) may be needed before the

 *  ^C (03) or after it.

 */

extern char Myattn[];



extern FILE *in;



/* Ward Christensen / CP/M parameters - Don't change these! */

#define ENQ 005

#define CAN ('X'&037)

#define XOFF ('s'&037)

#define XON ('q'&037)

#define SOH 1

#define STX 2

#define EOT 4

#define ACK 6

#define NAK 025

#define CPMEOF 032

#define WANTCRC 0103	/* send C not NAK to get crc not checksum */

#define WANTG 0107	/* Send G not NAK to get nonstop batch xmsn */

#define TIMEOUT (-2)

#define RCDO (-3)

#define RETRYMAX 10

#define SECSIZ 128	/* cp/m's Magic Number record size */

#define KSIZE 1024



extern char Lastrx;

extern char Crcflg;

extern int Wcsmask;

extern int Verbose;

extern int Modem;		/* MODEM - don't send pathnames */

extern int Restricted;	/* restricted; no /.. or ../ in filenames */

extern int Quiet;		/* overrides logic that would otherwise set verbose */

extern int Ascii;		/* Add CR's for brain damaged programs */

extern int Fullname;		/* transmit full pathname */

extern int Unlinkafter;	/* Unlink file after it is sent */

extern int Dottoslash;	/* Change foo.bar.baz to foo/bar/baz */

extern int firstsec;

extern int errcnt;		/* number of files unreadable */

extern int blklen;		/* length of transmitted records */

extern int Optiong;		/* Let it rip no wait for sector ACK's */

extern int Noeofseen;

extern int Totsecs;		/* total number of sectors this file */

extern char txbuf[];

extern int Filcnt;		/* count of number of files opened */

extern int Lfseen;

extern unsigned Rxbuflen;	/* Receiver's max buffer length */

extern int Tframlen;	/* Override for tx frame length */

extern int blkopt;		/* Override value for zmodem blklen */

extern int Rxflags;

extern int Wantfcs32;	/* want to send 32 bit FCS */

extern char Lzconv;	/* Local ZMODEM file conversion request */

extern char Lzmanag;	/* Local ZMODEM file management request */

extern char Lztrans;

extern char zconv;		/* ZMODEM file conversion request */

extern char zmanag;		/* ZMODEM file management request */

extern char ztrans;		/* ZMODEM file transport request */

extern int Command;		/* Send a command, then exit. */

extern char *Cmdstr;		/* Pointer to the command string */

extern int Cmdtries;

extern int Cmdack1;		/* Rx ACKs command, then do it */

extern int Exitcode;

extern int Testattn;		/* Force receiver to send Attn, etc with qbf. */

extern char *qbf;

extern long Lastread;		/* Beginning offset of last buffer read */

extern int Lastc;		/* Count of last buffer read or -1 */

extern int Dontread;		/* Don't read the buffer, it's still there */



extern jmp_buf tohere;		/* For the interrupt on RX timeout */

extern jmp_buf intrjmp;	/* For the interrupt on RX CAN */

extern int alrm();

/*

 * readock(timeout, count) reads character(s) from file descriptor 0

 *  (1 <= count <= 3)

 * it attempts to read count characters. If it gets more than one,

 * it is an error unless all are CAN

 * (otherwise, only normal response is ACK, CAN, or C)

 *  Only looks for one if Optiong, which signifies cbreak, not raw input

 *

 * timeout is in tenths of seconds

 */

readock(timeout, count)

{

	register int c;

	static char byt[5];



	if (Optiong)

		count = 1;	/* Special hack for cbreak */



	fflush(stdout);

	if (setjmp(tohere)) {

		logent("TIMEOUT\n");

		return TIMEOUT;

	}

	c = timeout/10;

	if (c<2)

		c=2;

	if (Verbose>3) {

		fprintf(stderr, "Timeout=%d Calling alarm(%d) ", timeout, c);

		byt[1] = 0;

	}

	signal(SIGALRM, alrm); alarm(c);

#ifdef ONEREAD

	c=read(iofd, byt, 1);		/* regulus raw read is unique */

#else

	c=read(iofd, byt, count);

#endif

	alarm(0);

	if (Verbose>5)

		fprintf(stderr, "ret cnt=%d %x %x\n", c, byt[0], byt[1]);

	if (c<1)

		return TIMEOUT;

	if (c==1)

		return (byt[0]&0377);

	else

		while (c)

			if (byt[--c] != CAN)

				return ERROR;

	return CAN;

}

readline(n)

{

	return (readock(n, 1));

}





purgeline()

{

#ifdef USG

	ioctl(iofd, TCFLSH, 0);

#else

	lseek(iofd, 0L, 2);

#endif

}





/* send cancel string to get the other end to shut up */

canit()

{

	static char canistr[] = {

	 24,24,24,24,24,24,24,24,24,24,8,8,8,8,8,8,8,8,8,8,0

	};



	printf(canistr);

	fflush(stdout);

}



/*VARARGS1*/

logent(a, b, c)

char *a, *b, *c;

{

	if(Verbose)

		fprintf(stderr, a, b, c);

}



/*

 * return 1 iff stdout and stderr are different devices

 *  indicating this program operating with a modem on a

 *  different line

 */

from_cu()

{

	struct stat a, b;

	fstat(1, &a); fstat(2, &b);

	return (a.st_rdev != b.st_rdev);

}



/*

 * substr(string, token) searches for token in string s

 * returns pointer to token within string if found, NULL otherwise

 */

char *

substr(s, t)

register char *s,*t;

{

	register char *ss,*tt;

	/* search for first char of token */

	for (ss=s; *s; s++)

		if (*s == *t)

			/* compare token with substring */

			for (ss=s,tt=t; ;) {

				if (*tt == 0)

					return s;

				if (*ss++ != *tt++)

					break;

			}

	return NULL;

}



char *babble[] = {

	"Send file(s) with ZMODEM/YMODEM/XMODEM Protocol",

	"	(Y) = Option applies to YMODEM only",

	"	(Z) = Option applies to ZMODEM only",

	"Usage:	sz [-12+aBbdefkLlNnquvXy] [-] file ...",

	"	sz [-1Beqv] -c COMMAND",

	"	1 Use stdout for modem input",

#ifdef CSTOPB

	"	2 Use 2 stop bits",

#endif

	"	+ Append to existing destination file (Z)",

	"	a (ASCII) change NL to CR/LF",

	"	b Binary file transfer override",

	"	c send COMMAND (Z)",

	"	d Change '.' to '/' in pathnames (Y/Z)",

	"	e Escape all control characters (Z)",

	"	f send Full pathname (Y/Z)",

	"	i send COMMAND, ack Immediately (Z)",

	"	k Send 1024 byte packets (Y)",

	"	L N Limit subpacket length to N bytes (Z)",

	"	l N Limit frame length to N bytes (l>=L) (Z)",

	"	n send file if source Newer or longer (Z)",

	"	N send file if source different length or date (Z)",

	"	o Use 16 bit CRC instead of 32 bit CRC (Z)",

	"	p Protect existing destination file (Z)",

	"	r Resume/Recover interrupted file transfer (Z)",

	"	q Quiet (no progress reports)",

	"	u Unlink file after transmission",

	"	v Verbose - debugging information",

	"	X XMODEM protocol - send no pathnames",

	"	y Yes, overwrite existing file (Z)",

	"- as pathname sends standard input as sPID.sz or environment ONAME",

	""

};



usage()

{

	char **pp;



	for (pp=babble; **pp; ++pp)

		fprintf(stderr, "%s\n", *pp);

	fprintf(stderr, "%s for %s by Chuck Forsberg  ", VERSION, OS);

	exit(1);

}



/*

 * Get the receiver's init parameters

 */

getzrxinit()

{

	register n;

	struct stat f;



	for (n=10; --n>=0; ) {

		

		switch (zgethdr(Rxhdr, 1)) {

		case ZCHALLENGE:	/* Echo receiver's challenge numbr */

			stohdr(Rxpos);

			zshhdr(ZACK, Txhdr);

			continue;

		case ZCOMMAND:		/* They didn't see out ZRQINIT */

			stohdr(0L);

			zshhdr(ZRQINIT, Txhdr);

			continue;

		case ZRINIT:

			Rxflags = 0377 & Rxhdr[ZF0];

			Txfcs32 = (Wantfcs32 && (Rxflags & CANFC32));

			Rxbuflen = (0377 & Rxhdr[ZP0])+((0377 & Rxhdr[ZP1])<<8);

			vfile("Rxbuflen=%d Tframlen=%d", Rxbuflen, Tframlen);

			if ( !Fromcu)

				signal(SIGINT, SIG_IGN);

#ifdef USG

			mode(2);	/* Set cbreak, XON/XOFF, etc. */

#endif

#ifndef READCHECK

#ifndef USG

			/* Use 1024 byte frames if no sample/interrupt */

			if (Rxbuflen < 32 || Rxbuflen > 1024) {

				Rxbuflen = 1024;

				vfile("Rxbuflen=%d", Rxbuflen);

			}

#endif

#endif

			/* Override to force shorter frame length */

			if (Rxbuflen && (Rxbuflen>Tframlen) && (Tframlen>=32))

				Rxbuflen = Tframlen;

			if ( !Rxbuflen && (Tframlen>=32) && (Tframlen<=1024))

				Rxbuflen = Tframlen;

			vfile("Rxbuflen=%d", Rxbuflen);



			/* If using a pipe for testing set lower buf len */

			fstat(iofd, &f);

			if ((f.st_mode & S_IFMT) != S_IFCHR

			  && (Rxbuflen == 0 || Rxbuflen > 4096))

				Rxbuflen = 4096;

			/*

			 * If input is not a regular file, force ACK's each 1024

			 *  (A smarter strategey could be used here ...)

			 */

			fstat(fileno(in), &f);

			if (((f.st_mode & S_IFMT) != S_IFREG)

			  && (Rxbuflen == 0 || Rxbuflen > 1024))

				Rxbuflen = 1024;

			vfile("Rxbuflen=%d", Rxbuflen);



			return (sendzsinit());

		case ZCAN:

		case TIMEOUT:

			return ERROR;

		case ZRQINIT:

			if (Rxhdr[ZF0] == ZCOMMAND)

				continue;

		default:

			zshhdr(ZNAK, Txhdr);

			continue;

		}

	}

	return ERROR;

}



/* Send send-init information */

sendzsinit()

{

	register c;

	register errors;



	if (Myattn[0] == '\0')

		return OK;

	errors = 0;

	for (;;) {

		stohdr(0L);

		zsbhdr(ZSINIT, Txhdr);

		zsdata(Myattn, 1+strlen(Myattn), ZCRCW);

		c = zgethdr(Rxhdr, 1);

		switch (c) {

		case ZCAN:

			return ERROR;

		case ZACK:

			return OK;

		default:

			if (++errors > 9)

				return ERROR;

			continue;

		}

	}

}



/* Send file name and related info */

zsendfile(buf, blen)

char *buf;

{

	register c;



	for (;;) {

		Txhdr[ZF0] = Lzconv;	/* file conversion request */

		Txhdr[ZF1] = Lzmanag;	/* file management request */

		Txhdr[ZF2] = Lztrans;	/* file transport request */

		Txhdr[ZF3] = 0;

		zsbhdr(ZFILE, Txhdr);

		zsdata(buf, blen, ZCRCW);

again:

		c = zgethdr(Rxhdr, 1);

		switch (c) {

		case ZRINIT:

			goto again;

		case ZCAN:

		case TIMEOUT:

		case ZABORT:

		case ZFIN:

			return ERROR;

		case ZSKIP:

			fclose(in); return c;

		case ZRPOS:

			fseek(in, Rxpos, 0);

			Txpos = Rxpos; Lastc = -1; Dontread = FALSE;

			return zsendfdata();

		default:

			continue;

		}

	}

}



/* Send the data in the file */

zsendfdata()

{

	register c, e;

	register newcnt;

	register long tcount = 0;

	static int tleft = 6;	/* Counter for test mode */



	if (Baudrate > 300)

		blklen = 256;

	if (Baudrate > 2400)

		blklen = KSIZE;

	if (Rxbuflen && blklen>Rxbuflen)

		blklen = Rxbuflen;

	if (blkopt && blklen > blkopt)

		blklen = blkopt;

	vfile("Rxbuflen=%d blklen=%d", Rxbuflen, blklen);

somemore:

	if (setjmp(intrjmp)) {

waitack:

		c = getinsync();

		switch (c) {

		default:

		case ZCAN:

			fclose(in);

			return ERROR;

		case ZSKIP:

			fclose(in);

			return c;

		case ZACK:

		case ZRPOS:

			break;

		case ZRINIT:

			return OK;

		}

#ifdef READCHECK

		/*

		 * If the reverse channel can be tested for data,

		 *  this logic may be used to detect error packets

		 *  sent by the receiver, in place of setjmp/longjmp

		 *  rdchk(fdes) returns non 0 if a character is available

		 */

		while (rdchk(iofd)) {

#ifdef SVR2

			switch (checked)

#else

			switch (readline(1))

#endif

			{

			case CAN:

			case ZPAD:

				goto waitack;

			case XOFF:		/* Wait a while for an XON */

			case XOFF|0200:

				readline(100);

			}

		}

#endif

	}



	if ( !Fromcu)

		signal(SIGINT, onintr);

	newcnt = Rxbuflen;

	stohdr(Txpos);

	zsbhdr(ZDATA, Txhdr);



	/*

	 * Special testing mode.  This should force receiver to Attn,ZRPOS

	 *  many times.  Each time the signal should be caught, causing the

	 *  file to be started over from the beginning.

	 */

	if (Testattn) {

		if ( --tleft)

			while (tcount < 20000) {

				printf(qbf); fflush(stdout);

				tcount += strlen(qbf);

#ifdef READCHECK

				while (rdchk(iofd)) {

#ifdef SVR2

					switch (checked)

#else

					switch (readline(1))

#endif

					{

					case CAN:

					case ZPAD:

#ifdef TCFLSH

						ioctl(iofd, TCFLSH, 1);

#endif

						goto waitack;

					case XOFF:	/* Wait for XON */

					case XOFF|0200:

						readline(100);

					}

				}

#endif

			}

		signal(SIGINT, SIG_IGN); canit();

		sleep(3); purgeline(); mode(0);

		printf("\nsz: Tcount = %ld\n", tcount);

		if (tleft) {

			printf("ERROR: Interrupts Not Caught\n");

			exit(1);

		}

		exit(0);

	}



	do {

		if (Dontread) {

			c = Lastc;

		} else {

			c = zfilbuf(txbuf, blklen);

			Lastread = Txpos;  Lastc = c;

		}

		if (Verbose > 10)

			vfile("Dontread=%d c=%d", Dontread, c);

		Dontread = FALSE;

		if (c < blklen)

			e = ZCRCE;

		else if (Rxbuflen && (newcnt -= c) <= 0)

			e = ZCRCW;

		else

			e = ZCRCG;

		zsdata(txbuf, c, e);

		Txpos += c;

		if (e == ZCRCW)

			goto waitack;

#ifdef READCHECK

		/*

		 * If the reverse channel can be tested for data,

		 *  this logic may be used to detect error packets

		 *  sent by the receiver, in place of setjmp/longjmp

		 *  rdchk(fdes) returns non 0 if a character is available

		 */

		fflush(stdout);

		while (rdchk(iofd)) {

#ifdef SVR2

			switch (checked)

#else

			switch (readline(1))

#endif

			{

			case CAN:

			case ZPAD:

#ifdef TCFLSH

				ioctl(iofd, TCFLSH, 1);

#endif

				/* zcrce - dinna wanna start a ping-pong game */

				zsdata(txbuf, 0, ZCRCE);

				goto waitack;

			case XOFF:		/* Wait a while for an XON */

			case XOFF|0200:

				readline(100);

			}

		}

#endif

	} while (c == blklen);

	if ( !Fromcu)

		signal(SIGINT, SIG_IGN);



	for (;;) {

		stohdr(Txpos);

		zsbhdr(ZEOF, Txhdr);

		switch (getinsync()) {

		case ZACK:

			continue;

		case ZRPOS:

			goto somemore;

		case ZRINIT:

			return OK;

		case ZSKIP:

			fclose(in);

			return c;

		default:

			fclose(in);

			return ERROR;

		}

	}

}



/*

 * Respond to receiver's complaint, get back in sync with receiver

 */

getinsync()

{

	register c;



	for (;;) {

		if (Testattn) {

			printf("\r\n\n\n***** Signal Caught *****\r\n");

			Rxpos = 0; c = ZRPOS;

		} else

			c = zgethdr(Rxhdr, 0);

		switch (c) {

		case ZCAN:

		case ZABORT:

		case ZFIN:

		case TIMEOUT:

			return ERROR;

		case ZRPOS:

			if (Lastc >= 0 && Lastread == Rxpos) {

				Dontread = TRUE;

			} else {

				clearerr(in);	/* In case file EOF seen */

				fseek(in, Rxpos, 0);

			}

			Txpos = Rxpos;

			return c;

		case ZACK:

			return c;

		case ZRINIT:

		case ZSKIP:

			fclose(in);

			return c;

		case ERROR:

		default:

			zsbhdr(ZNAK, Txhdr);

			continue;

		}

	}

}

/* Say "bibi" to the receiver, try to do it cleanly */

saybibi()

{

	for (;;) {

		stohdr(0L);

		zsbhdr(ZFIN, Txhdr);

		switch (zgethdr(Rxhdr, 0)) {

		case ZFIN:

			sendline('O'); sendline('O'); flushmo();

		case ZCAN:

		case TIMEOUT:

			return;

		}

	}

}



/* Local screen character display function */

bttyout(c)

{

	if (Verbose)

		putc(c, stderr);

}



/* Send command and related info */

zsendcmd(buf, blen)

char *buf;

{

	register c, errors;

	long cmdnum;



	cmdnum = getpid();

	errors = 0;

	for (;;) {

		stohdr(cmdnum);

		Txhdr[ZF0] = Cmdack1;

		zsbhdr(ZCOMMAND, Txhdr);

		zsdata(buf, blen, ZCRCW);

listen:

		Rxtimeout = 100;		/* Ten second wait for resp. */

		c = zgethdr(Rxhdr, 1);



		switch (c) {

		case ZRINIT:

			continue;

		case ERROR:

		case TIMEOUT:

			if (++errors > Cmdtries)

				return ERROR;

			continue;

		case ZCAN:

		case ZABORT:

		case ZFIN:

		case ZSKIP:

		case ZRPOS:

			return ERROR;

		default:

			if (++errors > 10)

				return ERROR;

			continue;

		case ZCOMPL:

			Exitcode = Rxpos;

			saybibi();

			return OK;

		case ZRQINIT:

			vfile("******** RZ *******");

			system("rz");

			vfile("******** SZ *******");

			goto listen;

		}

	}

}



/*

 * If called as sb use YMODEM protocol

 */

chkinvok(s)

char *s;

{

	register char *p;



	p = s;

	while (*p == '-')

		s = ++p;

	while (*p)

		if (*p++ == '/')

			s = p;

	if (*s == 'v') {

		Verbose=1; ++s;

	}

	Progname = s;

	if (s[0]=='s' && s[1]=='b') {

		Nozmodem = TRUE; blklen=KSIZE;

	}

}



[ RETURN TO DIRECTORY ]