xref: /minix/minix/commands/zmodem/sz.c (revision ab712d19)
1 #define VERSION "sz 2.12 05-29-88"
2 #define PUBDIR "/usr/spool/uucppublic"
3 
4 /*% cc -compat -M2 -Ox -K -i -DTXBSIZE=16384  -DNFGVMIN -DREADCHECK sz.c -lx -o sz; size sz
5 
6 	Following is used for testing, might not be reasonable for production
7 <-xtx-*> cc -Osal -DTXBSIZE=32768  -DSV sz.c -lx -o $B/sz; size $B/sz
8 
9  ****************************************************************************
10  *
11  * sz.c By Chuck Forsberg,  Omen Technology INC
12  *
13  ****************************************************************************
14  *
15  * Typical Unix/Xenix/Clone compiles:
16  *
17  *	cc -O sz.c -o sz		USG (SYS III/V) Unix
18  *	cc -O -DSV sz.c -o sz		Sys V Release 2 with non-blocking input
19  *					Define to allow reverse channel checking
20  *	cc -O -DV7  sz.c -o sz		Unix Version 7, 2.8 - 4.3 BSD
21  *
22  *	cc -O -K -i -DNFGVMIN -DREADCHECK sz.c -lx -o sz	Classic Xenix
23  *
24  *	ln sz sb			**** All versions ****
25  *	ln sz sx			**** All versions ****
26  *
27  ****************************************************************************
28  *
29  * Typical VMS compile and install sequence:
30  *
31  *		define LNK$LIBRARY   SYS$LIBRARY:VAXCRTL.OLB
32  *		cc sz.c
33  *		cc vvmodem.c
34  *		link sz,vvmodem
35  *	sz :== $disk$user2:[username.subdir]sz.exe
36  *
37  *  If you feel adventureous, remove the #define BADSYNC line
38  *  immediately following the #ifdef vax11c line!  Some VMS
39  *  systems know how to fseek, some don't.
40  *
41  ****************************************************************************
42  *
43  *
44  * A program for Unix to send files and commands to computers running
45  *  Professional-YAM, PowerCom, YAM, IMP, or programs supporting Y/XMODEM.
46  *
47  *  Sz uses buffered I/O to greatly reduce CPU time compared to UMODEM.
48  *
49  *  USG UNIX (3.0) ioctl conventions courtesy Jeff Martin
50  *
51  *  2.1x hacks to avoid VMS fseek() bogosity, allow input from pipe
52  *     -DBADSEEK -DTXBSIZE=32768
53  *  2.x has mods for VMS flavor
54  *
55  * 1.34 implements tx backchannel garbage count and ZCRCW after ZRPOS
56  * in accordance with the 7-31-87 ZMODEM Protocol Description
57  */
58 
59 
60 #include <sys/types.h>
61 
62 #ifdef vax11c
63 #define BADSEEK
64 #define TXBSIZE 32768		/* Must be power of two, < MAXINT */
65 #include <types.h>
66 #include <stat.h>
67 #define LOGFILE "szlog.tmp"
68 #define OS "VMS"
69 #define READCHECK
70 #define BUFWRITE
71 #define iofd
72 extern int errno;
73 #define SS_NORMAL SS$_NORMAL
74 #define xsendline(c) sendline(c)
75 
76 
77 #else	/* vax11c */
78 
79 
80 #define SS_NORMAL 0
81 #define LOGFILE "/tmp/szlog"
82 
83 #define sendline(c) putchar((c) & 0377)
84 #define xsendline(c) putchar(c)
85 
86 #endif
87 
88 #include <signal.h>
89 #include <setjmp.h>
90 #include <ctype.h>
91 #include <errno.h>
92 #include <stdlib.h>
93 #include <string.h>
94 #include <stdlib.h>
95 #include <unistd.h>
96 #include <utime.h>
97 #include <stdio.h>
98 #include <stdarg.h>
99 
100 #define PATHLEN 256
101 #define OK 0
102 #define FALSE 0
103 #define TRUE 1
104 #undef ERROR
105 #define ERROR (-1)
106 /* Ward Christensen / CP/M parameters - Don't change these! */
107 #define ENQ 005
108 #define CAN ('X'&037)
109 #define XOFF ('s'&037)
110 #define XON ('q'&037)
111 #define SOH 1
112 #define STX 2
113 #define EOT 4
114 #define ACK 6
115 #define NAK 025
116 #define CPMEOF 032
117 #define WANTCRC 0103	/* send C not NAK to get crc not checksum */
118 #define WANTG 0107	/* Send G not NAK to get nonstop batch xmsn */
119 #define TIMEOUT (-2)
120 #define RCDO (-3)
121 #define RETRYMAX 10
122 
123 
124 #define HOWMANY 2
125 int Zmodem=0;		/* ZMODEM protocol requested by receiver */
126 unsigned Baudrate=2400;	/* Default, should be set by first mode() call */
127 unsigned Txwindow;	/* Control the size of the transmitted window */
128 unsigned Txwspac;	/* Spacing between zcrcq requests */
129 unsigned Txwcnt;	/* Counter used to space ack requests */
130 long Lrxpos;		/* Receiver's last reported offset */
131 int errors;
132 
133 #ifdef vax11c
134 #include "vrzsz.c"	/* most of the system dependent stuff here */
135 #else
136 #include "rbsb.c"	/* most of the system dependent stuff here */
137 #endif
138 #include "crctab.c"
139 
140 int Filesleft;
141 long Totalleft;
142 
143 /*
144  * Attention string to be executed by receiver to interrupt streaming data
145  *  when an error is detected.  A pause (0336) may be needed before the
146  *  ^C (03) or after it.
147  */
148 #ifdef READCHECK
149 char Myattn[] = { 0 };
150 #else
151 #ifdef USG
152 char Myattn[] = { 03, 0336, 0 };
153 #else
154 char Myattn[] = { 0 };
155 #endif
156 #endif
157 
158 FILE *in;
159 
160 #ifdef BADSEEK
161 int Canseek = 0;	/* 1: Can seek 0: only rewind -1: neither (pipe) */
162 #ifndef TXBSIZE
163 #define TXBSIZE 16384		/* Must be power of two, < MAXINT */
164 #endif
165 #else
166 int Canseek = 1;	/* 1: Can seek 0: only rewind -1: neither (pipe) */
167 #endif
168 
169 #ifdef TXBSIZE
170 #define TXBMASK (TXBSIZE-1)
171 char Txb[TXBSIZE];		/* Circular buffer for file reads */
172 char *txbuf = Txb;		/* Pointer to current file segment */
173 #else
174 char txbuf[1024];
175 #endif
176 long vpos = 0;			/* Number of bytes read from file */
177 
178 char Lastrx;
179 char Crcflg;
180 int Verbose=0;
181 int Modem2=0;		/* XMODEM Protocol - don't send pathnames */
182 int Restricted=0;	/* restricted; no /.. or ../ in filenames */
183 int Quiet=0;		/* overrides logic that would otherwise set verbose */
184 int Ascii=0;		/* Add CR's for brain damaged programs */
185 int Fullname=0;		/* transmit full pathname */
186 int Unlinkafter=0;	/* Unlink file after it is sent */
187 int Dottoslash=0;	/* Change foo.bar.baz to foo/bar/baz */
188 int firstsec;
189 int errcnt=0;		/* number of files unreadable */
190 int blklen=128;		/* length of transmitted records */
191 int Optiong;		/* Let it rip no wait for sector ACK's */
192 int Eofseen;		/* EOF seen on input set by zfilbuf */
193 int BEofseen;		/* EOF seen on input set by fooseek */
194 int Totsecs;		/* total number of sectors this file */
195 int Filcnt=0;		/* count of number of files opened */
196 int Lfseen=0;
197 unsigned Rxbuflen = 16384;	/* Receiver's max buffer length */
198 int Tframlen = 0;	/* Override for tx frame length */
199 int blkopt=0;		/* Override value for zmodem blklen */
200 int Rxflags = 0;
201 long bytcnt;
202 int Wantfcs32 = TRUE;	/* want to send 32 bit FCS */
203 char Lzconv;	/* Local ZMODEM file conversion request */
204 char Lzmanag;	/* Local ZMODEM file management request */
205 int Lskipnocor;
206 char Lztrans;
207 char zconv;		/* ZMODEM file conversion request */
208 char zmanag;		/* ZMODEM file management request */
209 char ztrans;		/* ZMODEM file transport request */
210 int Command;		/* Send a command, then exit. */
211 char *Cmdstr;		/* Pointer to the command string */
212 int Cmdtries = 11;
213 int Cmdack1;		/* Rx ACKs command, then do it */
214 int Exitcode = 0;
215 int Test;		/* 1= Force receiver to send Attn, etc with qbf. */
216 			/* 2= Character transparency test */
217 char *qbf="The quick brown fox jumped over the lazy dog's back 1234567890\r\n";
218 long Lastsync;		/* Last offset to which we got a ZRPOS */
219 int Beenhereb4;		/* How many times we've been ZRPOS'd same place */
220 
221 jmp_buf tohere;		/* For the interrupt on RX timeout */
222 jmp_buf intrjmp;	/* For the interrupt on RX CAN */
223 
224 void onintr(int sig );
225 int main(int argc , char *argv []);
226 int wcsend(int argc , char *argp []);
227 int wcs(char *oname );
228 int wctxpn(char *name );
229 int getnak(void);
230 int wctx(long flen );
231 int wcputsec(char *buf , int sectnum , int cseclen );
232 int filbuf(char *buf , int count );
233 int zfilbuf(void);
234 int fooseek(FILE *fptr , long pos , int whence );
235 void alrm(int sig );
236 int readline(int timeout );
237 void flushmo(void);
238 void purgeline(void);
239 void canit(void);
240 void zperr();
241 char *substr(char *s , char *t );
242 int usage(void);
243 int getzrxinit(void);
244 int sendzsinit(void);
245 int zsendfile(char *buf , int blen );
246 int zsendfdata(void);
247 int getinsync(int flag );
248 void saybibi(void);
249 void bttyout(int c );
250 int zsendcmd(char *buf , int blen );
251 void chkinvok(char *s );
252 void countem(int argc , char **argv );
253 void chartest(int m );
254 
255 /* called by signal interrupt or terminate to clean things up */
bibi(int n)256 void bibi(int n)
257 {
258 	canit(); fflush(stdout); mode(0);
259 	fprintf(stderr, "sz: caught signal %d; exiting\n", n);
260 	if (n == SIGQUIT)
261 		abort();
262 	if (n == 99)
263 		fprintf(stderr, "mode(2) in rbsb.c not implemented!!\n");
264 	cucheck();
265 	exit(128+n);
266 }
267 /* Called when ZMODEM gets an interrupt (^X) */
onintr(int sig)268 void onintr(int sig)
269 {
270 	signal(SIGINT, SIG_IGN);
271 	longjmp(intrjmp, -1);
272 }
273 
274 int Zctlesc;	/* Encode control characters */
275 int Nozmodem = 0;	/* If invoked as "sb" */
276 char *Progname = "sz";
277 int Zrwindow = 1400;	/* RX window size (controls garbage count) */
278 #include "zm.c"
279 
280 
main(int argc,char * argv[])281 int main(int argc, char *argv[])
282 {
283 	register char *cp;
284 	register int npats;
285 	int dm;
286 	char **patts;
287 	static char xXbuf[BUFSIZ];
288 
289 	if ((cp = getenv("ZNULLS")) && *cp)
290 		Znulls = atoi(cp);
291 	if ((cp=getenv("SHELL")) && (substr(cp, "rsh") || substr(cp, "rksh")))
292 		Restricted=TRUE;
293 	from_cu();
294 	chkinvok(argv[0]);
295 
296 	Rxtimeout = 600;
297 	npats=0;
298 	if (argc<2)
299 		usage();
300 	setbuf(stdout, xXbuf);
301 	while (--argc) {
302 		cp = *++argv;
303 		if (*cp++ == '-' && *cp) {
304 			while ( *cp) {
305 				switch(*cp++) {
306 				case '\\':
307 					 *cp = toupper(*cp);  continue;
308 				case '+':
309 					Lzmanag = ZMAPND; break;
310 #ifdef CSTOPB
311 				case '2':
312 					Twostop = TRUE; break;
313 #endif
314 				case 'a':
315 					Lzconv = ZCNL;
316 					Ascii = TRUE; break;
317 				case 'b':
318 					Lzconv = ZCBIN; break;
319 				case 'C':
320 					if (--argc < 1) {
321 						usage();
322 					}
323 					Cmdtries = atoi(*++argv);
324 					break;
325 				case 'i':
326 					Cmdack1 = ZCACK1;
327 					/* **** FALL THROUGH TO **** */
328 				case 'c':
329 					if (--argc != 1) {
330 						usage();
331 					}
332 					Command = TRUE;
333 					Cmdstr = *++argv;
334 					break;
335 				case 'd':
336 					++Dottoslash;
337 					/* **** FALL THROUGH TO **** */
338 				case 'f':
339 					Fullname=TRUE; break;
340 				case 'e':
341 					Zctlesc = 1; break;
342 				case 'k':
343 					blklen=1024; break;
344 				case 'L':
345 					if (--argc < 1) {
346 						usage();
347 					}
348 					blkopt = atoi(*++argv);
349 					if (blkopt<24 || blkopt>1024)
350 						usage();
351 					break;
352 				case 'l':
353 					if (--argc < 1) {
354 						usage();
355 					}
356 					Tframlen = atoi(*++argv);
357 					if (Tframlen<32 || Tframlen>1024)
358 						usage();
359 					break;
360 				case 'N':
361 					Lzmanag = ZMNEWL;  break;
362 				case 'n':
363 					Lzmanag = ZMNEW;  break;
364 				case 'o':
365 					Wantfcs32 = FALSE; break;
366 				case 'p':
367 					Lzmanag = ZMPROT;  break;
368 				case 'r':
369 					Lzconv = ZCRESUM;
370 				case 'q':
371 					Quiet=TRUE; Verbose=0; break;
372 				case 't':
373 					if (--argc < 1) {
374 						usage();
375 					}
376 					Rxtimeout = atoi(*++argv);
377 					if (Rxtimeout<10 || Rxtimeout>1000)
378 						usage();
379 					break;
380 				case 'T':
381 					if (++Test > 1) {
382 						chartest(1); chartest(2);
383 						mode(0);  exit(0);
384 					}
385 					break;
386 #ifndef vax11c
387 				case 'u':
388 					++Unlinkafter; break;
389 #endif
390 				case 'v':
391 					++Verbose; break;
392 				case 'w':
393 					if (--argc < 1) {
394 						usage();
395 					}
396 					Txwindow = atoi(*++argv);
397 					if (Txwindow < 256)
398 						Txwindow = 256;
399 					Txwindow = (Txwindow/64) * 64;
400 					Txwspac = Txwindow/4;
401 					if (blkopt > Txwspac
402 					 || (!blkopt && Txwspac < 1024))
403 						blkopt = Txwspac;
404 					break;
405 				case 'X':
406 					++Modem2; break;
407 				case 'Y':
408 					Lskipnocor = TRUE;
409 					/* **** FALLL THROUGH TO **** */
410 				case 'y':
411 					Lzmanag = ZMCLOB; break;
412 				default:
413 					usage();
414 				}
415 			}
416 		}
417 		else if ( !npats && argc>0) {
418 			if (argv[0][0]) {
419 				npats=argc;
420 				patts=argv;
421 #ifndef vax11c
422 				if ( !strcmp(*patts, "-"))
423 					iofd = 1;
424 #endif
425 			}
426 		}
427 	}
428 	if (npats < 1 && !Command && !Test)
429 		usage();
430 	if (Verbose) {
431 		if (freopen(LOGFILE, "a", stderr)==NULL) {
432 			printf("Can't open log file %s\n",LOGFILE);
433 			exit(0200);
434 		}
435 		setbuf(stderr, (char *)NULL);
436 	}
437 	if (Fromcu && !Quiet) {
438 		if (Verbose == 0)
439 			Verbose = 2;
440 	}
441 	vfile("%s %s for %s\n", Progname, VERSION, OS);
442 
443 	mode(1);
444 
445 	if (signal(SIGINT, bibi) == SIG_IGN) {
446 		signal(SIGINT, SIG_IGN); signal(SIGKILL, SIG_IGN);
447 	} else {
448 		signal(SIGINT, bibi); signal(SIGKILL, bibi);
449 	}
450 	if ( !Fromcu)
451 		signal(SIGQUIT, SIG_IGN);
452 	signal(SIGTERM, bibi);
453 
454 	if ( !Modem2) {
455 		if (!Nozmodem) {
456 			printf("rz\r");  fflush(stdout);
457 		}
458 		countem(npats, patts);
459 		if (!Nozmodem) {
460 			stohdr(0L);
461 			if (Command)
462 				Txhdr[ZF0] = ZCOMMAND;
463 			zshhdr(ZRQINIT, Txhdr);
464 		}
465 	}
466 	fflush(stdout);
467 
468 	if (Command) {
469 		if (getzrxinit()) {
470 			Exitcode=0200; canit();
471 		}
472 		else if (zsendcmd(Cmdstr, 1+strlen(Cmdstr))) {
473 			Exitcode=0200; canit();
474 		}
475 	} else if (wcsend(npats, patts)==ERROR) {
476 		Exitcode=0200;
477 		canit();
478 	}
479 	fflush(stdout);
480 	mode(0);
481 	dm = ((errcnt != 0) | Exitcode);
482 	if (dm) {
483 		cucheck();  exit(dm);
484 	}
485 	putc('\n',stderr);
486 	exit(SS_NORMAL);
487 	/*NOTREACHED*/
488 }
489 
wcsend(int argc,char * argp[])490 int wcsend(int argc, char *argp[])
491 {
492 	register int n;
493 
494 	Crcflg=FALSE;
495 	firstsec=TRUE;
496 	bytcnt = -1;
497 	for (n=0; n<argc; ++n) {
498 		Totsecs = 0;
499 		if (wcs(argp[n])==ERROR)
500 			return ERROR;
501 	}
502 	Totsecs = 0;
503 	if (Filcnt==0) {	/* bitch if we couldn't open ANY files */
504 		if ( !Modem2) {
505 			Command = TRUE;
506 			Cmdstr = "echo \"sz: Can't open any requested files\"";
507 			if (getnak()) {
508 				Exitcode=0200; canit();
509 			}
510 			if (!Zmodem)
511 				canit();
512 			else if (zsendcmd(Cmdstr, 1+strlen(Cmdstr))) {
513 				Exitcode=0200; canit();
514 			}
515 			Exitcode = 1; return OK;
516 		}
517 		canit();
518 		fprintf(stderr,"\r\nCan't open any requested files.\r\n");
519 		return ERROR;
520 	}
521 	if (Zmodem)
522 		saybibi();
523 	else if ( !Modem2)
524 		wctxpn("");
525 	return OK;
526 }
527 
wcs(char * oname)528 int wcs(char *oname)
529 {
530 	register int c;
531 	register char *p;
532 	struct stat f;
533 	char name[PATHLEN];
534 
535 	strcpy(name, oname);
536 
537 	if (Restricted) {
538 		/* restrict pathnames to current tree or uucppublic */
539 		if ( substr(name, "../")
540 		 || (name[0]== '/' && strncmp(name, PUBDIR, strlen(PUBDIR))) ) {
541 			canit();
542 			fprintf(stderr,"\r\nsz:\tSecurity Violation\r\n");
543 			return ERROR;
544 		}
545 	}
546 
547 	if ( !strcmp(oname, "-")) {
548 		if ((p = getenv("ONAME")) && *p)
549 			strcpy(name, p);
550 		else
551 			sprintf(name, "s%d.sz", getpid());
552 		in = stdin;
553 	}
554 	else if ((in=fopen(oname, "r"))==NULL) {
555 		++errcnt;
556 		return OK;	/* pass over it, there may be others */
557 	}
558 	BEofseen = Eofseen = 0;  vpos = 0;
559 	/* Check for directory or block special files */
560 	fstat(fileno(in), &f);
561 	c = f.st_mode & S_IFMT;
562 	if (c == S_IFDIR || c == S_IFBLK) {
563 		fclose(in);
564 		return OK;
565 	}
566 
567 	++Filcnt;
568 	switch (wctxpn(name)) {
569 	case ERROR:
570 		return ERROR;
571 	case ZSKIP:
572 		return OK;
573 	}
574 	if (!Zmodem && wctx(f.st_size)==ERROR)
575 		return ERROR;
576 #ifndef vax11c
577 	if (Unlinkafter)
578 		unlink(oname);
579 #endif
580 	return 0;
581 }
582 
583 /*
584  * generate and transmit pathname block consisting of
585  *  pathname (null terminated),
586  *  file length, mode time and file mode in octal
587  *  as provided by the Unix fstat call.
588  *  N.B.: modifies the passed name, may extend it!
589  */
wctxpn(char * name)590 int wctxpn(char *name)
591 {
592 	register char *p, *q;
593 	char name2[PATHLEN];
594 	struct stat f;
595 
596 	if (Modem2) {
597 		if ((in!=stdin) && *name && fstat(fileno(in), &f)!= -1) {
598 			fprintf(stderr, "Sending %s, %lld blocks: ",
599 			  name, f.st_size>>7);
600 		}
601 		fprintf(stderr, "Give your local XMODEM receive command now.\r\n");
602 		return OK;
603 	}
604 	zperr("Awaiting pathname nak for %s", *name?name:"<END>");
605 	if ( !Zmodem)
606 		if (getnak())
607 			return ERROR;
608 
609 	q = (char *) 0;
610 	if (Dottoslash) {		/* change . to . */
611 		for (p=name; *p; ++p) {
612 			if (*p == '/')
613 				q = p;
614 			else if (*p == '.')
615 				*(q=p) = '/';
616 		}
617 		if (q && strlen(++q) > 8) {	/* If name>8 chars */
618 			q += 8;			/*   make it .ext */
619 			strcpy(name2, q);	/* save excess of name */
620 			*q = '.';
621 			strcpy(++q, name2);	/* add it back */
622 		}
623 	}
624 
625 	for (p=name, q=txbuf ; *p; )
626 		if ((*q++ = *p++) == '/' && !Fullname)
627 			q = txbuf;
628 	*q++ = 0;
629 	p=q;
630 	while (q < (txbuf + 1024))
631 		*q++ = 0;
632 	if (!Ascii && (in!=stdin) && *name && fstat(fileno(in), &f)!= -1)
633 		sprintf(p, "%llu %llo %o 0 %d %ld", f.st_size, f.st_mtime,
634 		  f.st_mode, Filesleft, Totalleft);
635 	Totalleft -= f.st_size;
636 	if (--Filesleft <= 0)
637 		Totalleft = 0;
638 	if (Totalleft < 0)
639 		Totalleft = 0;
640 
641 	/* force 1k blocks if name won't fit in 128 byte block */
642 	if (txbuf[125])
643 		blklen=1024;
644 	else {		/* A little goodie for IMP/KMD */
645 		txbuf[127] = (f.st_size + 127) >>7;
646 		txbuf[126] = (f.st_size + 127) >>15;
647 	}
648 	if (Zmodem)
649 		return zsendfile(txbuf, 1+strlen(p)+(p-txbuf));
650 	if (wcputsec(txbuf, 0, 128)==ERROR)
651 		return ERROR;
652 	return OK;
653 }
654 
getnak()655 int getnak()
656 {
657 	register int firstch;
658 
659 	Lastrx = 0;
660 	for (;;) {
661 		switch (firstch = readline(800)) {
662 		case ZPAD:
663 			if (getzrxinit())
664 				return ERROR;
665 			Ascii = 0;	/* Receiver does the conversion */
666 			return FALSE;
667 		case TIMEOUT:
668 			zperr("Timeout on pathname");
669 			return TRUE;
670 		case WANTG:
671 #ifdef MODE2OK
672 			mode(2);	/* Set cbreak, XON/XOFF, etc. */
673 #endif
674 			Optiong = TRUE;
675 			blklen=1024;
676 		case WANTCRC:
677 			Crcflg = TRUE;
678 		case NAK:
679 			return FALSE;
680 		case CAN:
681 			if ((firstch = readline(20)) == CAN && Lastrx == CAN)
682 				return TRUE;
683 		default:
684 			break;
685 		}
686 		Lastrx = firstch;
687 	}
688 }
689 
690 
wctx(long flen)691 int wctx(long flen)
692 {
693 	register int thisblklen;
694 	register int sectnum, attempts, firstch;
695 	long charssent;
696 
697 	charssent = 0;  firstsec=TRUE;  thisblklen = blklen;
698 	vfile("wctx:file length=%ld", flen);
699 
700 	while ((firstch=readline(Rxtimeout))!=NAK && firstch != WANTCRC
701 	  && firstch != WANTG && firstch!=TIMEOUT && firstch!=CAN)
702 		;
703 	if (firstch==CAN) {
704 		zperr("Receiver CANcelled");
705 		return ERROR;
706 	}
707 	if (firstch==WANTCRC)
708 		Crcflg=TRUE;
709 	if (firstch==WANTG)
710 		Crcflg=TRUE;
711 	sectnum=0;
712 	for (;;) {
713 		if (flen <= (charssent + 896L))
714 			thisblklen = 128;
715 		if ( !filbuf(txbuf, thisblklen))
716 			break;
717 		if (wcputsec(txbuf, ++sectnum, thisblklen)==ERROR)
718 			return ERROR;
719 		charssent += thisblklen;
720 	}
721 	fclose(in);
722 	attempts=0;
723 	do {
724 		purgeline();
725 		sendline(EOT);
726 		fflush(stdout);
727 		++attempts;
728 	}
729 		while ((firstch = readline(Rxtimeout)) != ACK &&
730 		    attempts < RETRYMAX);
731 	if (attempts == RETRYMAX) {
732 		zperr("No ACK on EOT");
733 		return ERROR;
734 	}
735 	else
736 		return OK;
737 }
738 /**
739  * @param cseclen :data length of this sector to send
740  */
wcputsec(char * buf,int sectnum,int cseclen)741 int wcputsec(char *buf, int sectnum, int cseclen)
742 {
743 	register int checksum, wcj;
744 	register char *cp;
745 	unsigned oldcrc;
746 	int firstch;
747 	int attempts;
748 
749 	firstch=0;	/* part of logic to detect CAN CAN */
750 
751 	if (Verbose>2)
752 		fprintf(stderr, "Sector %3d %2dk\n", Totsecs, Totsecs/8 );
753 	else if (Verbose>1)
754 		fprintf(stderr, "\rSector %3d %2dk ", Totsecs, Totsecs/8 );
755 	for (attempts=0; attempts <= RETRYMAX; attempts++) {
756 		Lastrx= firstch;
757 		sendline(cseclen==1024?STX:SOH);
758 		sendline(sectnum);
759 		sendline(-sectnum -1);
760 		oldcrc=checksum=0;
761 		for (wcj=cseclen,cp=buf; --wcj>=0; ) {
762 			sendline(*cp);
763 			oldcrc=updcrc((0377& *cp), oldcrc);
764 			checksum += *cp++;
765 		}
766 		if (Crcflg) {
767 			oldcrc=updcrc(0,updcrc(0,oldcrc));
768 			sendline((int)oldcrc>>8);
769 			sendline((int)oldcrc);
770 		}
771 		else
772 			sendline(checksum);
773 
774 		if (Optiong) {
775 			firstsec = FALSE; return OK;
776 		}
777 		firstch = readline(Rxtimeout);
778 gotnak:
779 		switch (firstch) {
780 		case CAN:
781 			if(Lastrx == CAN) {
782 cancan:
783 				zperr("Cancelled");  return ERROR;
784 			}
785 			break;
786 		case TIMEOUT:
787 			zperr("Timeout on sector ACK"); continue;
788 		case WANTCRC:
789 			if (firstsec)
790 				Crcflg = TRUE;
791 		case NAK:
792 			zperr("NAK on sector"); continue;
793 		case ACK:
794 			firstsec=FALSE;
795 			Totsecs += (cseclen>>7);
796 			return OK;
797 		case ERROR:
798 			zperr("Got burst for sector ACK"); break;
799 		default:
800 			zperr("Got %02x for sector ACK", firstch); break;
801 		}
802 		for (;;) {
803 			Lastrx = firstch;
804 			if ((firstch = readline(Rxtimeout)) == TIMEOUT)
805 				break;
806 			if (firstch == NAK || firstch == WANTCRC)
807 				goto gotnak;
808 			if (firstch == CAN && Lastrx == CAN)
809 				goto cancan;
810 		}
811 	}
812 	zperr("Retry Count Exceeded");
813 	return ERROR;
814 }
815 
816 /* fill buf with count chars padding with ^Z for CPM */
filbuf(char * buf,int count)817 int filbuf(char *buf, int count)
818 {
819 	register int c, m;
820 
821 	if ( !Ascii) {
822 		m = read(fileno(in), buf, count);
823 		if (m <= 0)
824 			return 0;
825 		while (m < count)
826 			buf[m++] = 032;
827 		return count;
828 	}
829 	m=count;
830 	if (Lfseen) {
831 		*buf++ = 012; --m; Lfseen = 0;
832 	}
833 	while ((c=getc(in))!=EOF) {
834 		if (c == 012) {
835 			*buf++ = 015;
836 			if (--m == 0) {
837 				Lfseen = TRUE; break;
838 			}
839 		}
840 		*buf++ =c;
841 		if (--m == 0)
842 			break;
843 	}
844 	if (m==count)
845 		return 0;
846 	else
847 		while (--m>=0)
848 			*buf++ = CPMEOF;
849 	return count;
850 }
851 
852 /* Fill buffer with blklen chars */
zfilbuf()853 int zfilbuf()
854 {
855 	int n;
856 
857 #ifdef TXBSIZE
858 	/* We assume request is within buffer, or just beyond */
859 	txbuf = Txb + (bytcnt & TXBMASK);
860 	if (vpos <= bytcnt) {
861 		n = fread(txbuf, 1, blklen, in);
862 		vpos += n;
863 		if (n < blklen)
864 			Eofseen = 1;
865 		return n;
866 	}
867 	if (vpos >= (bytcnt+blklen))
868 		return blklen;
869 	/* May be a short block if crash recovery etc. */
870 	Eofseen = BEofseen;
871 	return (vpos - bytcnt);
872 #else
873 	n = fread(txbuf, 1, blklen, in);
874 	if (n < blklen)
875 		Eofseen = 1;
876 	return n;
877 #endif
878 }
879 
880 #ifdef TXBSIZE
fooseek(FILE * fptr,long pos,int whence)881 int fooseek(FILE *fptr, long pos, int whence)
882 {
883 	int m, n;
884 
885 	vfile("fooseek: pos =%lu vpos=%lu Canseek=%d", pos, vpos, Canseek);
886 	/* Seek offset < current buffer */
887 	if (pos < (vpos -TXBSIZE +1024)) {
888 		BEofseen = 0;
889 		if (Canseek > 0) {
890 			vpos = pos & ~TXBMASK;
891 			if (vpos >= pos)
892 				vpos -= TXBSIZE;
893 			if (fseek(fptr, vpos, 0))
894 				return 1;
895 		}
896 		else if (Canseek == 0)
897 			if (fseek(fptr, vpos = 0L, 0))
898 				return 1;
899 		else
900 			return 1;
901 		while (vpos <= pos) {
902 			n = fread(Txb, 1, TXBSIZE, fptr);
903 			vpos += n;
904 			vfile("n=%d vpos=%ld", n, vpos);
905 			if (n < TXBSIZE) {
906 				BEofseen = 1;
907 				break;
908 			}
909 		}
910 		vfile("vpos=%ld", vpos);
911 		return 0;
912 	}
913 	/* Seek offset > current buffer (crash recovery, etc.) */
914 	if (pos > vpos) {
915 		if (Canseek)
916 			if (fseek(fptr, vpos = (pos & ~TXBMASK), 0))
917 				return 1;
918 		while (vpos <= pos) {
919 			txbuf = Txb + (vpos & TXBMASK);
920 			m = TXBSIZE - (vpos & TXBMASK);
921 			n = fread(txbuf, 1, m, fptr);
922 			vpos += n;
923 			vfile("bo=%d n=%d vpos=%ld", txbuf-Txb, n, vpos);
924 			if (m < n) {
925 				BEofseen = 1;
926 				break;
927 			}
928 		}
929 		return 0;
930 	}
931 	/* Seek offset is within current buffer */
932 	vfile("vpos=%ld", vpos);
933 	return 0;
934 }
935 #define fseek fooseek
936 #endif
937 
vfile(const char * string,...)938 void vfile(const char *string, ...)
939 {
940 	if (Verbose > 2) {
941 		va_list args;
942 		va_start(args, string);
943 		vfprintf(stderr, string, args);
944 		va_end(args);
945 		fprintf(stderr, "\n");
946 	}
947 }
948 
949 
alrm(int sig)950 void alrm(int sig)
951 {
952 	longjmp(tohere, -1);
953 }
954 
955 
956 #ifndef vax11c
957 /*
958  * readline(timeout) reads character(s) from file descriptor 0
959  * timeout is in tenths of seconds
960  */
readline(int timeout)961 int readline(int timeout)
962 {
963 	register int c;
964 	static char byt[1];
965 
966 	fflush(stdout);
967 	if (setjmp(tohere)) {
968 		zperr("TIMEOUT");
969 		return TIMEOUT;
970 	}
971 	c = timeout/10;
972 	if (c<2)
973 		c=2;
974 	if (Verbose>5) {
975 		fprintf(stderr, "Timeout=%d Calling alarm(%d) ", timeout, c);
976 	}
977 	signal(SIGALRM, alrm); alarm(c);
978 	c=read(iofd, byt, 1);
979 	alarm(0);
980 	if (Verbose>5)
981 		fprintf(stderr, "ret %x\n", byt[0]);
982 	if (c<1)
983 		return TIMEOUT;
984 	return (byt[0]&0377);
985 }
986 
flushmo()987 void flushmo()
988 {
989 	fflush(stdout);
990 }
991 
992 
purgeline()993 void purgeline()
994 {
995 #ifdef USG
996 	ioctl(iofd, TCFLSH, 0);
997 #else
998 	lseek(iofd, 0L, 2);
999 #endif
1000 }
1001 #endif
1002 
1003 /* send cancel string to get the other end to shut up */
canit()1004 void canit()
1005 {
1006 	static char canistr[] = {
1007 	 24,24,24,24,24,24,24,24,24,24,8,8,8,8,8,8,8,8,8,8,0
1008 	};
1009 
1010 #ifdef vax11c
1011 	raw_wbuf(strlen(canistr), canistr);
1012 	purgeline();
1013 #else
1014 	printf("%s", canistr);
1015 	fflush(stdout);
1016 #endif
1017 }
1018 
1019 
1020 /*
1021  * Log an error
1022  */
1023 /*VARARGS1*/
zperr(char * s,char * p,char * u)1024 void zperr(char *s, char *p, char *u)
1025 {
1026 	if (Verbose <= 0)
1027 		return;
1028 	fprintf(stderr, "\nRetry %d: ", errors);
1029 	fprintf(stderr, s, p, u);
1030 	fprintf(stderr, "\n");
1031 }
1032 
1033 /*
1034  * substr(string, token) searches for token in string s
1035  * returns pointer to token within string if found, NULL otherwise
1036  */
1037 char *
substr(char * s,char * t)1038 substr(char *s, char *t)
1039 {
1040 	register char *ss,*tt;
1041 	/* search for first char of token */
1042 	for (ss=s; *s; s++)
1043 		if (*s == *t)
1044 			/* compare token with substring */
1045 			for (ss=s,tt=t; ;) {
1046 				if (*tt == 0)
1047 					return s;
1048 				if (*ss++ != *tt++)
1049 					break;
1050 			}
1051 	return (char *)NULL;
1052 }
1053 
1054 char *babble[] = {
1055 #ifdef vax11c
1056 	"	Send file(s) with ZMODEM Protocol",
1057 	"Usage:	sz [-2+abdefkLlNnquvwYy] [-] file ...",
1058 	"	sz [-2Ceqv] -c COMMAND",
1059 	"	\\ Force next option letter to upper case",
1060 #else
1061 	"Send file(s) with ZMODEM/YMODEM/XMODEM Protocol",
1062 	"	(Y) = Option applies to YMODEM only",
1063 	"	(Z) = Option applies to ZMODEM only",
1064 	"Usage:	sz [-2+abdefkLlNnquvwYy] [-] file ...",
1065 	"	sz [-2Ceqv] -c COMMAND",
1066 	"	sb [-2adfkquv] [-] file ...",
1067 	"	sx [-2akquv] [-] file",
1068 #endif
1069 #ifdef CSTOPB
1070 	"	2 Use 2 stop bits",
1071 #endif
1072 	"	+ Append to existing destination file (Z)",
1073 	"	a (ASCII) change NL to CR/LF",
1074 	"	b Binary file transfer override",
1075 	"	c send COMMAND (Z)",
1076 #ifndef vax11c
1077 	"	d Change '.' to '/' in pathnames (Y/Z)",
1078 #endif
1079 	"	e Escape all control characters (Z)",
1080 	"	f send Full pathname (Y/Z)",
1081 	"	i send COMMAND, ack Immediately (Z)",
1082 	"	k Send 1024 byte packets (Y)",
1083 	"	L N Limit subpacket length to N bytes (Z)",
1084 	"	l N Limit frame length to N bytes (l>=L) (Z)",
1085 	"	n send file if source newer (Z)",
1086 	"	N send file if source newer or longer (Z)",
1087 	"	o Use 16 bit CRC instead of 32 bit CRC (Z)",
1088 	"	p Protect existing destination file (Z)",
1089 	"	r Resume/Recover interrupted file transfer (Z)",
1090 	"	q Quiet (no progress reports)",
1091 #ifndef vax11c
1092 	"	u Unlink file after transmission",
1093 #endif
1094 	"	v Verbose - provide debugging information",
1095 	"	w N Window is N bytes (Z)",
1096 	"	Y Yes, overwrite existing file, skip if not present at rx (Z)",
1097 	"	y Yes, overwrite existing file (Z)",
1098 	"- as pathname sends standard input as sPID.sz or environment ONAME",
1099 	""
1100 };
1101 
usage()1102 int usage()
1103 {
1104 	char **pp;
1105 
1106 	for (pp=babble; **pp; ++pp)
1107 		fprintf(stderr, "%s\n", *pp);
1108 	fprintf(stderr, "%s for %s by Chuck Forsberg, Omen Technology INC\n",
1109 	 VERSION, OS);
1110 	fprintf(stderr, "\t\t\042The High Reliability Software\042\n");
1111 	cucheck();
1112 	exit(SS_NORMAL);
1113 }
1114 
1115 /*
1116  * Get the receiver's init parameters
1117  */
getzrxinit()1118 int getzrxinit()
1119 {
1120 	register int n;
1121 	struct stat f;
1122 
1123 	for (n=10; --n>=0; ) {
1124 
1125 		switch (zgethdr(Rxhdr, 1)) {
1126 		case ZCHALLENGE:	/* Echo receiver's challenge numbr */
1127 			stohdr(Rxpos);
1128 			zshhdr(ZACK, Txhdr);
1129 			continue;
1130 		case ZCOMMAND:		/* They didn't see out ZRQINIT */
1131 			stohdr(0L);
1132 			zshhdr(ZRQINIT, Txhdr);
1133 			continue;
1134 		case ZRINIT:
1135 			Rxflags = 0377 & Rxhdr[ZF0];
1136 			Txfcs32 = (Wantfcs32 && (Rxflags & CANFC32));
1137 			Zctlesc |= Rxflags & TESCCTL;
1138 			Rxbuflen = (0377 & Rxhdr[ZP0])+((0377 & Rxhdr[ZP1])<<8);
1139 			if ( !(Rxflags & CANFDX))
1140 				Txwindow = 0;
1141 			vfile("Rxbuflen=%d Tframlen=%d", Rxbuflen, Tframlen);
1142 			if ( !Fromcu)
1143 				signal(SIGINT, SIG_IGN);
1144 #ifdef MODE2OK
1145 			mode(2);	/* Set cbreak, XON/XOFF, etc. */
1146 #endif
1147 #ifndef READCHECK
1148 #ifndef USG
1149 			/* Use 1024 byte frames if no sample/interrupt */
1150 			if (Rxbuflen < 32 || Rxbuflen > 1024) {
1151 				Rxbuflen = 1024;
1152 				vfile("Rxbuflen=%d", Rxbuflen);
1153 			}
1154 #endif
1155 #endif
1156 			/* Override to force shorter frame length */
1157 			if (Rxbuflen && (Rxbuflen>Tframlen) && (Tframlen>=32))
1158 				Rxbuflen = Tframlen;
1159 			if ( !Rxbuflen && (Tframlen>=32) && (Tframlen<=1024))
1160 				Rxbuflen = Tframlen;
1161 			vfile("Rxbuflen=%d", Rxbuflen);
1162 
1163 #ifndef vax11c
1164 			/* If using a pipe for testing set lower buf len */
1165 			fstat(iofd, &f);
1166 			if ((f.st_mode & S_IFMT) != S_IFCHR) {
1167 				Rxbuflen = 1024;
1168 			}
1169 #endif
1170 #ifdef BADSEEK
1171 			Canseek = 0;
1172 			Txwindow = TXBSIZE - 1024;
1173 			Txwspac = TXBSIZE/4;
1174 #endif
1175 			/*
1176 			 * If input is not a regular file, force ACK's to
1177 			 *  prevent running beyond the buffer limits
1178 			 */
1179 			if ( !Command) {
1180 				fstat(fileno(in), &f);
1181 				if ((f.st_mode & S_IFMT) != S_IFREG) {
1182 					Canseek = -1;
1183 #ifdef TXBSIZE
1184 					Txwindow = TXBSIZE - 1024;
1185 					Txwspac = TXBSIZE/4;
1186 #else
1187 					return ERROR;
1188 #endif
1189 				}
1190 			}
1191 			/* Set initial subpacket length */
1192 			if (blklen < 1024) {	/* Command line override? */
1193 				if (Baudrate > 300)
1194 					blklen = 256;
1195 				if (Baudrate > 1200)
1196 					blklen = 512;
1197 				if (Baudrate > 2400)
1198 					blklen = 1024;
1199 			}
1200 			if (Rxbuflen && blklen>Rxbuflen)
1201 				blklen = Rxbuflen;
1202 			if (blkopt && blklen > blkopt)
1203 				blklen = blkopt;
1204 			vfile("Rxbuflen=%d blklen=%d", Rxbuflen, blklen);
1205 			vfile("Txwindow = %u Txwspac = %d", Txwindow, Txwspac);
1206 
1207 			return (sendzsinit());
1208 		case ZCAN:
1209 		case TIMEOUT:
1210 			return ERROR;
1211 		case ZRQINIT:
1212 			if (Rxhdr[ZF0] == ZCOMMAND)
1213 				continue;
1214 		default:
1215 			zshhdr(ZNAK, Txhdr);
1216 			continue;
1217 		}
1218 	}
1219 	return ERROR;
1220 }
1221 
1222 /* Send send-init information */
sendzsinit()1223 int sendzsinit()
1224 {
1225 	register int c;
1226 
1227 	if (Myattn[0] == '\0' && (!Zctlesc || (Rxflags & TESCCTL)))
1228 		return OK;
1229 	errors = 0;
1230 	for (;;) {
1231 		stohdr(0L);
1232 		if (Zctlesc) {
1233 			Txhdr[ZF0] |= TESCCTL; zshhdr(ZSINIT, Txhdr);
1234 		}
1235 		else
1236 			zsbhdr(ZSINIT, Txhdr);
1237 		zsdata(Myattn, 1+strlen(Myattn), ZCRCW);
1238 		c = zgethdr(Rxhdr, 1);
1239 		switch (c) {
1240 		case ZCAN:
1241 			return ERROR;
1242 		case ZACK:
1243 			return OK;
1244 		default:
1245 			if (++errors > 19)
1246 				return ERROR;
1247 			continue;
1248 		}
1249 	}
1250 }
1251 
1252 /* Send file name and related info */
zsendfile(char * buf,int blen)1253 int zsendfile(char *buf, int blen)
1254 {
1255 	register int c;
1256 	register UNSL long crc;
1257 
1258 	for (;;) {
1259 		Txhdr[ZF0] = Lzconv;	/* file conversion request */
1260 		Txhdr[ZF1] = Lzmanag;	/* file management request */
1261 		if (Lskipnocor)
1262 			Txhdr[ZF1] |= ZMSKNOLOC;
1263 		Txhdr[ZF2] = Lztrans;	/* file transport request */
1264 		Txhdr[ZF3] = 0;
1265 		zsbhdr(ZFILE, Txhdr);
1266 		zsdata(buf, blen, ZCRCW);
1267 again:
1268 		c = zgethdr(Rxhdr, 1);
1269 		switch (c) {
1270 		case ZRINIT:
1271 			while ((c = readline(50)) > 0)
1272 				if (c == ZPAD) {
1273 					goto again;
1274 				}
1275 			/* **** FALL THRU TO **** */
1276 		default:
1277 			continue;
1278 		case ZCAN:
1279 		case TIMEOUT:
1280 		case ZABORT:
1281 		case ZFIN:
1282 			return ERROR;
1283 		case ZCRC:
1284 			crc = 0xFFFFFFFFL;
1285 			if (Canseek >= 0) {
1286 				while (((c = getc(in)) != EOF) && --Rxpos)
1287 					crc = UPDC32(c, crc);
1288 				crc = ~crc;
1289 				clearerr(in);	/* Clear EOF */
1290 				fseek(in, 0L, 0);
1291 			}
1292 			stohdr(crc);
1293 			zsbhdr(ZCRC, Txhdr);
1294 			goto again;
1295 		case ZSKIP:
1296 			fclose(in); return c;
1297 		case ZRPOS:
1298 			/*
1299 			 * Suppress zcrcw request otherwise triggered by
1300 			 * lastyunc==bytcnt
1301 			 */
1302 			if (Rxpos && fseek(in, Rxpos, 0))
1303 				return ERROR;
1304 			Lastsync = (bytcnt = Txpos = Rxpos) -1;
1305 			return zsendfdata();
1306 		}
1307 	}
1308 }
1309 
1310 /* Send the data in the file */
zsendfdata()1311 int zsendfdata()
1312 {
1313 	register int c, e, n;
1314 	register int newcnt;
1315 	register long tcount = 0;
1316 	int junkcount;		/* Counts garbage chars received by TX */
1317 	static int tleft = 6;	/* Counter for test mode */
1318 
1319 	Lrxpos = 0;
1320 	junkcount = 0;
1321 	Beenhereb4 = FALSE;
1322 somemore:
1323 	if (setjmp(intrjmp)) {
1324 waitack:
1325 		junkcount = 0;
1326 		c = getinsync(0);
1327 gotack:
1328 		switch (c) {
1329 		default:
1330 		case ZCAN:
1331 			fclose(in);
1332 			return ERROR;
1333 		case ZSKIP:
1334 			fclose(in);
1335 			return c;
1336 		case ZACK:
1337 		case ZRPOS:
1338 			break;
1339 		case ZRINIT:
1340 			return OK;
1341 		}
1342 #ifdef READCHECK
1343 		/*
1344 		 * If the reverse channel can be tested for data,
1345 		 *  this logic may be used to detect error packets
1346 		 *  sent by the receiver, in place of setjmp/longjmp
1347 		 *  rdchk(fdes) returns non 0 if a character is available
1348 		 */
1349 		while (rdchk(iofd)) {
1350 #ifdef SV
1351 			switch (checked)
1352 #else
1353 			switch (readline(1))
1354 #endif
1355 			{
1356 			case CAN:
1357 			case ZPAD:
1358 				c = getinsync(1);
1359 				goto gotack;
1360 			case XOFF:		/* Wait a while for an XON */
1361 			case XOFF|0200:
1362 				readline(100);
1363 			}
1364 		}
1365 #endif
1366 	}
1367 
1368 	if ( !Fromcu)
1369 		signal(SIGINT, onintr);
1370 	newcnt = Rxbuflen;
1371 	Txwcnt = 0;
1372 	stohdr(Txpos);
1373 	zsbhdr(ZDATA, Txhdr);
1374 
1375 	/*
1376 	 * Special testing mode.  This should force receiver to Attn,ZRPOS
1377 	 *  many times.  Each time the signal should be caught, causing the
1378 	 *  file to be started over from the beginning.
1379 	 */
1380 	if (Test) {
1381 		if ( --tleft)
1382 			while (tcount < 20000) {
1383 				printf("%s", qbf); fflush(stdout);
1384 				tcount += strlen(qbf);
1385 #ifdef READCHECK
1386 				while (rdchk(iofd)) {
1387 #ifdef SV
1388 					switch (checked)
1389 #else
1390 					switch (readline(1))
1391 #endif
1392 					{
1393 					case CAN:
1394 					case ZPAD:
1395 #ifdef TCFLSH
1396 						ioctl(iofd, TCFLSH, 1);
1397 #endif
1398 						goto waitack;
1399 					case XOFF:	/* Wait for XON */
1400 					case XOFF|0200:
1401 						readline(100);
1402 					}
1403 				}
1404 #endif
1405 			}
1406 		signal(SIGINT, SIG_IGN); canit();
1407 		sleep(3); purgeline(); mode(0);
1408 		printf("\nsz: Tcount = %ld\n", tcount);
1409 		if (tleft) {
1410 			printf("ERROR: Interrupts Not Caught\n");
1411 			exit(1);
1412 		}
1413 		exit(SS_NORMAL);
1414 	}
1415 
1416 	do {
1417 		n = zfilbuf();
1418 		if (Eofseen)
1419 			e = ZCRCE;
1420 		else if (junkcount > 3)
1421 			e = ZCRCW;
1422 		else if (bytcnt == Lastsync)
1423 			e = ZCRCW;
1424 		else if (Rxbuflen && (newcnt -= n) <= 0)
1425 			e = ZCRCW;
1426 		else if (Txwindow && (Txwcnt += n) >= Txwspac) {
1427 			Txwcnt = 0;  e = ZCRCQ;
1428 		}
1429 		else
1430 			e = ZCRCG;
1431 		if (Verbose>1)
1432 			fprintf(stderr, "\r%7ld ZMODEM%s    ",
1433 			  Txpos, Crc32t?" CRC-32":"");
1434 		zsdata(txbuf, n, e);
1435 		bytcnt = Txpos += n;
1436 		if (e == ZCRCW)
1437 			goto waitack;
1438 #ifdef READCHECK
1439 		/*
1440 		 * If the reverse channel can be tested for data,
1441 		 *  this logic may be used to detect error packets
1442 		 *  sent by the receiver, in place of setjmp/longjmp
1443 		 *  rdchk(fdes) returns non 0 if a character is available
1444 		 */
1445 		fflush(stdout);
1446 		while (rdchk(iofd)) {
1447 #ifdef SV
1448 			switch (checked)
1449 #else
1450 			switch (readline(1))
1451 #endif
1452 			{
1453 			case CAN:
1454 			case ZPAD:
1455 				c = getinsync(1);
1456 				if (c == ZACK)
1457 					break;
1458 #ifdef TCFLSH
1459 				ioctl(iofd, TCFLSH, 1);
1460 #endif
1461 				/* zcrce - dinna wanna starta ping-pong game */
1462 				zsdata(txbuf, 0, ZCRCE);
1463 				goto gotack;
1464 			case XOFF:		/* Wait a while for an XON */
1465 			case XOFF|0200:
1466 				readline(100);
1467 			default:
1468 				++junkcount;
1469 			}
1470 		}
1471 #endif	/* READCHECK */
1472 		if (Txwindow) {
1473 			while ((tcount = Txpos - Lrxpos) >= Txwindow) {
1474 				vfile("%ld window >= %u", tcount, Txwindow);
1475 				if (e != ZCRCQ)
1476 					zsdata(txbuf, 0, e = ZCRCQ);
1477 				c = getinsync(1);
1478 				if (c != ZACK) {
1479 #ifdef TCFLSH
1480 					ioctl(iofd, TCFLSH, 1);
1481 #endif
1482 					zsdata(txbuf, 0, ZCRCE);
1483 					goto gotack;
1484 				}
1485 			}
1486 			vfile("window = %ld", tcount);
1487 		}
1488 	} while (!Eofseen);
1489 	if ( !Fromcu)
1490 		signal(SIGINT, SIG_IGN);
1491 
1492 	for (;;) {
1493 		stohdr(Txpos);
1494 		zsbhdr(ZEOF, Txhdr);
1495 		switch (getinsync(0)) {
1496 		case ZACK:
1497 			continue;
1498 		case ZRPOS:
1499 			goto somemore;
1500 		case ZRINIT:
1501 			return OK;
1502 		case ZSKIP:
1503 			fclose(in);
1504 			return c;
1505 		default:
1506 			fclose(in);
1507 			return ERROR;
1508 		}
1509 	}
1510 }
1511 
1512 /*
1513  * Respond to receiver's complaint, get back in sync with receiver
1514  */
getinsync(int flag)1515 int getinsync(int flag)
1516 {
1517 	register int c;
1518 
1519 	for (;;) {
1520 		if (Test) {
1521 			printf("\r\n\n\n***** Signal Caught *****\r\n");
1522 			Rxpos = 0; c = ZRPOS;
1523 		} else
1524 			c = zgethdr(Rxhdr, 0);
1525 		switch (c) {
1526 		case ZCAN:
1527 		case ZABORT:
1528 		case ZFIN:
1529 		case TIMEOUT:
1530 			return ERROR;
1531 		case ZRPOS:
1532 			/* ************************************* */
1533 			/*  If sending to a buffered modem, you  */
1534 			/*   might send a break at this point to */
1535 			/*   dump the modem's buffer.		 */
1536 			clearerr(in);	/* In case file EOF seen */
1537 			if (fseek(in, Rxpos, 0))
1538 				return ERROR;
1539 			Eofseen = 0;
1540 			bytcnt = Lrxpos = Txpos = Rxpos;
1541 			if (Lastsync == Rxpos) {
1542 				if (++Beenhereb4 > 4)
1543 					if (blklen > 32)
1544 						blklen /= 2;
1545 			}
1546 			Lastsync = Rxpos;
1547 			return c;
1548 		case ZACK:
1549 			Lrxpos = Rxpos;
1550 			if (flag || Txpos == Rxpos)
1551 				return ZACK;
1552 			continue;
1553 		case ZRINIT:
1554 		case ZSKIP:
1555 			fclose(in);
1556 			return c;
1557 		case ERROR:
1558 		default:
1559 			zsbhdr(ZNAK, Txhdr);
1560 			continue;
1561 		}
1562 	}
1563 }
1564 
1565 
1566 /* Say "bibi" to the receiver, try to do it cleanly */
saybibi()1567 void saybibi()
1568 {
1569 	for (;;) {
1570 		stohdr(0L);		/* CAF Was zsbhdr - minor change */
1571 		zshhdr(ZFIN, Txhdr);	/*  to make debugging easier */
1572 		switch (zgethdr(Rxhdr, 0)) {
1573 		case ZFIN:
1574 			sendline('O'); sendline('O'); flushmo();
1575 		case ZCAN:
1576 		case TIMEOUT:
1577 			return;
1578 		}
1579 	}
1580 }
1581 
1582 /* Local screen character display function */
bttyout(int c)1583 void bttyout(int c)
1584 {
1585 	if (Verbose)
1586 		putc(c, stderr);
1587 }
1588 
1589 /* Send command and related info */
zsendcmd(char * buf,int blen)1590 int zsendcmd(char *buf, int blen)
1591 {
1592 	register int c;
1593 	long cmdnum;
1594 
1595 	cmdnum = getpid();
1596 	errors = 0;
1597 	for (;;) {
1598 		stohdr(cmdnum);
1599 		Txhdr[ZF0] = Cmdack1;
1600 		zsbhdr(ZCOMMAND, Txhdr);
1601 		zsdata(buf, blen, ZCRCW);
1602 listen:
1603 		Rxtimeout = 100;		/* Ten second wait for resp. */
1604 		c = zgethdr(Rxhdr, 1);
1605 
1606 		switch (c) {
1607 		case ZRINIT:
1608 			goto listen;	/* CAF 8-21-87 */
1609 		case ERROR:
1610 		case TIMEOUT:
1611 			if (++errors > Cmdtries)
1612 				return ERROR;
1613 			continue;
1614 		case ZCAN:
1615 		case ZABORT:
1616 		case ZFIN:
1617 		case ZSKIP:
1618 		case ZRPOS:
1619 			return ERROR;
1620 		default:
1621 			if (++errors > 20)
1622 				return ERROR;
1623 			continue;
1624 		case ZCOMPL:
1625 			Exitcode = Rxpos;
1626 			saybibi();
1627 			return OK;
1628 		case ZRQINIT:
1629 #ifdef vax11c		/* YAMP :== Yet Another Missing Primitive */
1630 			return ERROR;
1631 #else
1632 			vfile("******** RZ *******");
1633 			system("rz");
1634 			vfile("******** SZ *******");
1635 			goto listen;
1636 #endif
1637 		}
1638 	}
1639 }
1640 
1641 /*
1642  * If called as sb use YMODEM protocol
1643  */
chkinvok(char * s)1644 void chkinvok(char *s)
1645 {
1646 #ifdef vax11c
1647 	Progname = "sz";
1648 #else
1649 	register char *p;
1650 
1651 	p = s;
1652 	while (*p == '-')
1653 		s = ++p;
1654 	while (*p)
1655 		if (*p++ == '/')
1656 			s = p;
1657 	if (*s == 'v') {
1658 		Verbose=1; ++s;
1659 	}
1660 	Progname = s;
1661 	if (s[0]=='s' && s[1]=='b') {
1662 		Nozmodem = TRUE; blklen=1024;
1663 	}
1664 	if (s[0]=='s' && s[1]=='x') {
1665 		Modem2 = TRUE;
1666 	}
1667 #endif
1668 }
1669 
countem(int argc,char ** argv)1670 void countem(int argc, char **argv)
1671 {
1672 	register int c;
1673 	struct stat f;
1674 
1675 	for (Totalleft = 0, Filesleft = 0; --argc >=0; ++argv) {
1676 		f.st_size = -1;
1677 		if (Verbose>2) {
1678 			fprintf(stderr, "\nCountem: %03d %s ", argc, *argv);
1679 			fflush(stderr);
1680 		}
1681 		if (access(*argv, 04) >= 0 && stat(*argv, &f) >= 0) {
1682 			c = f.st_mode & S_IFMT;
1683 			if (c != S_IFDIR && c != S_IFBLK) {
1684 				++Filesleft;  Totalleft += f.st_size;
1685 			}
1686 		}
1687 		if (Verbose>2)
1688 			fprintf(stderr, " %lld", f.st_size);
1689 	}
1690 	if (Verbose>2)
1691 		fprintf(stderr, "\ncountem: Total %d %ld\n",
1692 		  Filesleft, Totalleft);
1693 }
1694 
chartest(int m)1695 void chartest(int m)
1696 {
1697 	register int n;
1698 
1699 	mode(m);
1700 	printf("\r\n\nCharacter Transparency Test Mode %d\r\n", m);
1701 	printf("If Pro-YAM/ZCOMM is not displaying ^M hit ALT-V NOW.\r\n");
1702 	printf("Hit Enter.\021");  fflush(stdout);
1703 	readline(500);
1704 
1705 	for (n = 0; n < 256; ++n) {
1706 		if (!(n%8))
1707 			printf("\r\n");
1708 		printf("%02x ", n);  fflush(stdout);
1709 		sendline(n);	flushmo();
1710 		printf("  ");  fflush(stdout);
1711 		if (n == 127) {
1712 			printf("Hit Enter.\021");  fflush(stdout);
1713 			readline(500);
1714 			printf("\r\n");  fflush(stdout);
1715 		}
1716 	}
1717 	printf("\021\r\nEnter Characters, echo is in hex.\r\n");
1718 	printf("Hit SPACE or pause 40 seconds for exit.\r\n");
1719 
1720 	while (n != TIMEOUT && n != ' ') {
1721 		n = readline(400);
1722 		printf("%02x\r\n", n);
1723 		fflush(stdout);
1724 	}
1725 	printf("\r\nMode %d character transparency test ends.\r\n", m);
1726 	fflush(stdout);
1727 }
1728 
1729 /* End of sz.c */
1730