minix3/commands/zmodem/sz.c

1729 lines
36 KiB
C

#define VERSION "sz 2.12 05-29-88"
#define PUBDIR "/usr/spool/uucppublic"
/*% cc -compat -M2 -Ox -K -i -DTXBSIZE=16384 -DNFGVMIN -DREADCHECK sz.c -lx -o sz; size sz
Following is used for testing, might not be reasonable for production
<-xtx-*> cc -Osal -DTXBSIZE=32768 -DSV sz.c -lx -o $B/sz; size $B/sz
****************************************************************************
*
* sz.c By Chuck Forsberg, Omen Technology INC
*
****************************************************************************
*
* Typical Unix/Xenix/Clone compiles:
*
* cc -O sz.c -o sz USG (SYS III/V) Unix
* cc -O -DSV 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 Classic Xenix
*
* ln sz sb **** All versions ****
* ln sz sx **** All versions ****
*
****************************************************************************
*
* Typical VMS compile and install sequence:
*
* define LNK$LIBRARY SYS$LIBRARY:VAXCRTL.OLB
* cc sz.c
* cc vvmodem.c
* link sz,vvmodem
* sz :== $disk$user2:[username.subdir]sz.exe
*
* If you feel adventureous, remove the #define BADSYNC line
* immediately following the #ifdef vax11c line! Some VMS
* systems know how to fseek, some don't.
*
****************************************************************************
*
*
* 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
*
* 2.1x hacks to avoid VMS fseek() bogosity, allow input from pipe
* -DBADSEEK -DTXBSIZE=32768
* 2.x has mods for VMS flavor
*
* 1.34 implements tx backchannel garbage count and ZCRCW after ZRPOS
* in accordance with the 7-31-87 ZMODEM Protocol Description
*/
#include <sys/types.h>
#ifdef vax11c
#define BADSEEK
#define TXBSIZE 32768 /* Must be power of two, < MAXINT */
#include <types.h>
#include <stat.h>
#define LOGFILE "szlog.tmp"
#define OS "VMS"
#define READCHECK
#define BUFWRITE
#define iofd
extern int errno;
#define SS_NORMAL SS$_NORMAL
#define xsendline(c) sendline(c)
#else /* vax11c */
#define SS_NORMAL 0
#define LOGFILE "/tmp/szlog"
#define sendline(c) putchar((c) & 0377)
#define xsendline(c) putchar(c)
#endif
#include <signal.h>
#include <setjmp.h>
#include <ctype.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <utime.h>
#include <stdio.h>
#include <stdarg.h>
#define PATHLEN 256
#define OK 0
#define FALSE 0
#define TRUE 1
#undef ERROR
#define ERROR (-1)
/* 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 HOWMANY 2
int Zmodem=0; /* ZMODEM protocol requested by receiver */
unsigned Baudrate=2400; /* Default, should be set by first mode() call */
unsigned Txwindow; /* Control the size of the transmitted window */
unsigned Txwspac; /* Spacing between zcrcq requests */
unsigned Txwcnt; /* Counter used to space ack requests */
long Lrxpos; /* Receiver's last reported offset */
int errors;
#ifdef vax11c
#include "vrzsz.c" /* most of the system dependent stuff here */
#else
#include "rbsb.c" /* most of the system dependent stuff here */
#endif
#include "crctab.c"
int Filesleft;
long Totalleft;
/*
* 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.
*/
#ifdef READCHECK
char Myattn[] = { 0 };
#else
#ifdef USG
char Myattn[] = { 03, 0336, 0 };
#else
char Myattn[] = { 0 };
#endif
#endif
FILE *in;
#ifdef BADSEEK
int Canseek = 0; /* 1: Can seek 0: only rewind -1: neither (pipe) */
#ifndef TXBSIZE
#define TXBSIZE 16384 /* Must be power of two, < MAXINT */
#endif
#else
int Canseek = 1; /* 1: Can seek 0: only rewind -1: neither (pipe) */
#endif
#ifdef TXBSIZE
#define TXBMASK (TXBSIZE-1)
char Txb[TXBSIZE]; /* Circular buffer for file reads */
char *txbuf = Txb; /* Pointer to current file segment */
#else
char txbuf[1024];
#endif
long vpos = 0; /* Number of bytes read from file */
char Lastrx;
char Crcflg;
int Verbose=0;
int Modem2=0; /* XMODEM Protocol - don't send pathnames */
int Restricted=0; /* restricted; no /.. or ../ in filenames */
int Quiet=0; /* overrides logic that would otherwise set verbose */
int Ascii=0; /* Add CR's for brain damaged programs */
int Fullname=0; /* transmit full pathname */
int Unlinkafter=0; /* Unlink file after it is sent */
int Dottoslash=0; /* Change foo.bar.baz to foo/bar/baz */
int firstsec;
int errcnt=0; /* number of files unreadable */
int blklen=128; /* length of transmitted records */
int Optiong; /* Let it rip no wait for sector ACK's */
int Eofseen; /* EOF seen on input set by zfilbuf */
int BEofseen; /* EOF seen on input set by fooseek */
int Totsecs; /* total number of sectors this file */
int Filcnt=0; /* count of number of files opened */
int Lfseen=0;
unsigned Rxbuflen = 16384; /* Receiver's max buffer length */
int Tframlen = 0; /* Override for tx frame length */
int blkopt=0; /* Override value for zmodem blklen */
int Rxflags = 0;
long bytcnt;
int Wantfcs32 = TRUE; /* want to send 32 bit FCS */
char Lzconv; /* Local ZMODEM file conversion request */
char Lzmanag; /* Local ZMODEM file management request */
int Lskipnocor;
char Lztrans;
char zconv; /* ZMODEM file conversion request */
char zmanag; /* ZMODEM file management request */
char ztrans; /* ZMODEM file transport request */
int Command; /* Send a command, then exit. */
char *Cmdstr; /* Pointer to the command string */
int Cmdtries = 11;
int Cmdack1; /* Rx ACKs command, then do it */
int Exitcode = 0;
int Test; /* 1= Force receiver to send Attn, etc with qbf. */
/* 2= Character transparency test */
char *qbf="The quick brown fox jumped over the lazy dog's back 1234567890\r\n";
long Lastsync; /* Last offset to which we got a ZRPOS */
int Beenhereb4; /* How many times we've been ZRPOS'd same place */
jmp_buf tohere; /* For the interrupt on RX timeout */
jmp_buf intrjmp; /* For the interrupt on RX CAN */
void onintr(int sig );
int main(int argc , char *argv []);
int wcsend(int argc , char *argp []);
int wcs(char *oname );
int wctxpn(char *name );
int getnak(void);
int wctx(long flen );
int wcputsec(char *buf , int sectnum , int cseclen );
int filbuf(char *buf , int count );
int zfilbuf(void);
int fooseek(FILE *fptr , long pos , int whence );
void alrm(int sig );
int readline(int timeout );
void flushmo(void);
void purgeline(void);
void canit(void);
void zperr();
char *substr(char *s , char *t );
int usage(void);
int getzrxinit(void);
int sendzsinit(void);
int zsendfile(char *buf , int blen );
int zsendfdata(void);
int getinsync(int flag );
void saybibi(void);
void bttyout(int c );
int zsendcmd(char *buf , int blen );
void chkinvok(char *s );
void countem(int argc , char **argv );
void chartest(int m );
/* called by signal interrupt or terminate to clean things up */
void bibi(int n)
{
canit(); fflush(stdout); mode(0);
fprintf(stderr, "sz: caught signal %d; exiting\n", n);
if (n == SIGQUIT)
abort();
if (n == 99)
fprintf(stderr, "mode(2) in rbsb.c not implemented!!\n");
cucheck();
exit(128+n);
}
/* Called when ZMODEM gets an interrupt (^X) */
void onintr(int sig)
{
signal(SIGINT, SIG_IGN);
longjmp(intrjmp, -1);
}
int Zctlesc; /* Encode control characters */
int Nozmodem = 0; /* If invoked as "sb" */
char *Progname = "sz";
int Zrwindow = 1400; /* RX window size (controls garbage count) */
#include "zm.c"
int main(int argc, char *argv[])
{
register char *cp;
register int npats;
int dm;
char **patts;
static char xXbuf[BUFSIZ];
if ((cp = getenv("ZNULLS")) && *cp)
Znulls = atoi(cp);
if ((cp=getenv("SHELL")) && (substr(cp, "rsh") || substr(cp, "rksh")))
Restricted=TRUE;
from_cu();
chkinvok(argv[0]);
Rxtimeout = 600;
npats=0;
if (argc<2)
usage();
setbuf(stdout, xXbuf);
while (--argc) {
cp = *++argv;
if (*cp++ == '-' && *cp) {
while ( *cp) {
switch(*cp++) {
case '\\':
*cp = toupper(*cp); continue;
case '+':
Lzmanag = ZMAPND; break;
#ifdef CSTOPB
case '2':
Twostop = TRUE; break;
#endif
case 'a':
Lzconv = ZCNL;
Ascii = TRUE; break;
case 'b':
Lzconv = ZCBIN; break;
case 'C':
if (--argc < 1) {
usage();
}
Cmdtries = atoi(*++argv);
break;
case 'i':
Cmdack1 = ZCACK1;
/* **** FALL THROUGH TO **** */
case 'c':
if (--argc != 1) {
usage();
}
Command = TRUE;
Cmdstr = *++argv;
break;
case 'd':
++Dottoslash;
/* **** FALL THROUGH TO **** */
case 'f':
Fullname=TRUE; break;
case 'e':
Zctlesc = 1; break;
case 'k':
blklen=1024; break;
case 'L':
if (--argc < 1) {
usage();
}
blkopt = atoi(*++argv);
if (blkopt<24 || blkopt>1024)
usage();
break;
case 'l':
if (--argc < 1) {
usage();
}
Tframlen = atoi(*++argv);
if (Tframlen<32 || Tframlen>1024)
usage();
break;
case 'N':
Lzmanag = ZMNEWL; break;
case 'n':
Lzmanag = ZMNEW; break;
case 'o':
Wantfcs32 = FALSE; break;
case 'p':
Lzmanag = ZMPROT; break;
case 'r':
Lzconv = ZCRESUM;
case 'q':
Quiet=TRUE; Verbose=0; break;
case 't':
if (--argc < 1) {
usage();
}
Rxtimeout = atoi(*++argv);
if (Rxtimeout<10 || Rxtimeout>1000)
usage();
break;
case 'T':
if (++Test > 1) {
chartest(1); chartest(2);
mode(0); exit(0);
}
break;
#ifndef vax11c
case 'u':
++Unlinkafter; break;
#endif
case 'v':
++Verbose; break;
case 'w':
if (--argc < 1) {
usage();
}
Txwindow = atoi(*++argv);
if (Txwindow < 256)
Txwindow = 256;
Txwindow = (Txwindow/64) * 64;
Txwspac = Txwindow/4;
if (blkopt > Txwspac
|| (!blkopt && Txwspac < 1024))
blkopt = Txwspac;
break;
case 'X':
++Modem2; break;
case 'Y':
Lskipnocor = TRUE;
/* **** FALLL THROUGH TO **** */
case 'y':
Lzmanag = ZMCLOB; break;
default:
usage();
}
}
}
else if ( !npats && argc>0) {
if (argv[0][0]) {
npats=argc;
patts=argv;
#ifndef vax11c
if ( !strcmp(*patts, "-"))
iofd = 1;
#endif
}
}
}
if (npats < 1 && !Command && !Test)
usage();
if (Verbose) {
if (freopen(LOGFILE, "a", stderr)==NULL) {
printf("Can't open log file %s\n",LOGFILE);
exit(0200);
}
setbuf(stderr, (char *)NULL);
}
if (Fromcu && !Quiet) {
if (Verbose == 0)
Verbose = 2;
}
vfile("%s %s for %s\n", Progname, VERSION, OS);
mode(1);
if (signal(SIGINT, bibi) == SIG_IGN) {
signal(SIGINT, SIG_IGN); signal(SIGKILL, SIG_IGN);
} else {
signal(SIGINT, bibi); signal(SIGKILL, bibi);
}
if ( !Fromcu)
signal(SIGQUIT, SIG_IGN);
signal(SIGTERM, bibi);
if ( !Modem2) {
if (!Nozmodem) {
printf("rz\r"); fflush(stdout);
}
countem(npats, patts);
if (!Nozmodem) {
stohdr(0L);
if (Command)
Txhdr[ZF0] = ZCOMMAND;
zshhdr(ZRQINIT, Txhdr);
}
}
fflush(stdout);
if (Command) {
if (getzrxinit()) {
Exitcode=0200; canit();
}
else if (zsendcmd(Cmdstr, 1+strlen(Cmdstr))) {
Exitcode=0200; canit();
}
} else if (wcsend(npats, patts)==ERROR) {
Exitcode=0200;
canit();
}
fflush(stdout);
mode(0);
dm = ((errcnt != 0) | Exitcode);
if (dm) {
cucheck(); exit(dm);
}
putc('\n',stderr);
exit(SS_NORMAL);
/*NOTREACHED*/
}
int wcsend(int argc, char *argp[])
{
register int n;
Crcflg=FALSE;
firstsec=TRUE;
bytcnt = -1;
for (n=0; n<argc; ++n) {
Totsecs = 0;
if (wcs(argp[n])==ERROR)
return ERROR;
}
Totsecs = 0;
if (Filcnt==0) { /* bitch if we couldn't open ANY files */
if ( !Modem2) {
Command = TRUE;
Cmdstr = "echo \"sz: Can't open any requested files\"";
if (getnak()) {
Exitcode=0200; canit();
}
if (!Zmodem)
canit();
else if (zsendcmd(Cmdstr, 1+strlen(Cmdstr))) {
Exitcode=0200; canit();
}
Exitcode = 1; return OK;
}
canit();
fprintf(stderr,"\r\nCan't open any requested files.\r\n");
return ERROR;
}
if (Zmodem)
saybibi();
else if ( !Modem2)
wctxpn("");
return OK;
}
int wcs(char *oname)
{
register int c;
register char *p;
struct stat f;
char name[PATHLEN];
strcpy(name, oname);
if (Restricted) {
/* restrict pathnames to current tree or uucppublic */
if ( substr(name, "../")
|| (name[0]== '/' && strncmp(name, PUBDIR, strlen(PUBDIR))) ) {
canit();
fprintf(stderr,"\r\nsz:\tSecurity Violation\r\n");
return ERROR;
}
}
if ( !strcmp(oname, "-")) {
if ((p = getenv("ONAME")) && *p)
strcpy(name, p);
else
sprintf(name, "s%d.sz", getpid());
in = stdin;
}
else if ((in=fopen(oname, "r"))==NULL) {
++errcnt;
return OK; /* pass over it, there may be others */
}
BEofseen = Eofseen = 0; vpos = 0;
/* Check for directory or block special files */
fstat(fileno(in), &f);
c = f.st_mode & S_IFMT;
if (c == S_IFDIR || c == S_IFBLK) {
fclose(in);
return OK;
}
++Filcnt;
switch (wctxpn(name)) {
case ERROR:
return ERROR;
case ZSKIP:
return OK;
}
if (!Zmodem && wctx(f.st_size)==ERROR)
return ERROR;
#ifndef vax11c
if (Unlinkafter)
unlink(oname);
#endif
return 0;
}
/*
* generate and transmit pathname block consisting of
* pathname (null terminated),
* file length, mode time and file mode in octal
* as provided by the Unix fstat call.
* N.B.: modifies the passed name, may extend it!
*/
int wctxpn(char *name)
{
register char *p, *q;
char name2[PATHLEN];
struct stat f;
if (Modem2) {
if ((in!=stdin) && *name && fstat(fileno(in), &f)!= -1) {
fprintf(stderr, "Sending %s, %lld blocks: ",
name, f.st_size>>7);
}
fprintf(stderr, "Give your local XMODEM receive command now.\r\n");
return OK;
}
zperr("Awaiting pathname nak for %s", *name?name:"<END>");
if ( !Zmodem)
if (getnak())
return ERROR;
q = (char *) 0;
if (Dottoslash) { /* change . to . */
for (p=name; *p; ++p) {
if (*p == '/')
q = p;
else if (*p == '.')
*(q=p) = '/';
}
if (q && strlen(++q) > 8) { /* If name>8 chars */
q += 8; /* make it .ext */
strcpy(name2, q); /* save excess of name */
*q = '.';
strcpy(++q, name2); /* add it back */
}
}
for (p=name, q=txbuf ; *p; )
if ((*q++ = *p++) == '/' && !Fullname)
q = txbuf;
*q++ = 0;
p=q;
while (q < (txbuf + 1024))
*q++ = 0;
if (!Ascii && (in!=stdin) && *name && fstat(fileno(in), &f)!= -1)
sprintf(p, "%llu %o %o 0 %d %ld", f.st_size, f.st_mtime,
f.st_mode, Filesleft, Totalleft);
Totalleft -= f.st_size;
if (--Filesleft <= 0)
Totalleft = 0;
if (Totalleft < 0)
Totalleft = 0;
/* force 1k blocks if name won't fit in 128 byte block */
if (txbuf[125])
blklen=1024;
else { /* A little goodie for IMP/KMD */
txbuf[127] = (f.st_size + 127) >>7;
txbuf[126] = (f.st_size + 127) >>15;
}
if (Zmodem)
return zsendfile(txbuf, 1+strlen(p)+(p-txbuf));
if (wcputsec(txbuf, 0, 128)==ERROR)
return ERROR;
return OK;
}
int getnak()
{
register int firstch;
Lastrx = 0;
for (;;) {
switch (firstch = readline(800)) {
case ZPAD:
if (getzrxinit())
return ERROR;
Ascii = 0; /* Receiver does the conversion */
return FALSE;
case TIMEOUT:
zperr("Timeout on pathname");
return TRUE;
case WANTG:
#ifdef MODE2OK
mode(2); /* Set cbreak, XON/XOFF, etc. */
#endif
Optiong = TRUE;
blklen=1024;
case WANTCRC:
Crcflg = TRUE;
case NAK:
return FALSE;
case CAN:
if ((firstch = readline(20)) == CAN && Lastrx == CAN)
return TRUE;
default:
break;
}
Lastrx = firstch;
}
}
int wctx(long flen)
{
register int thisblklen;
register int sectnum, attempts, firstch;
long charssent;
charssent = 0; firstsec=TRUE; thisblklen = blklen;
vfile("wctx:file length=%ld", flen);
while ((firstch=readline(Rxtimeout))!=NAK && firstch != WANTCRC
&& firstch != WANTG && firstch!=TIMEOUT && firstch!=CAN)
;
if (firstch==CAN) {
zperr("Receiver CANcelled");
return ERROR;
}
if (firstch==WANTCRC)
Crcflg=TRUE;
if (firstch==WANTG)
Crcflg=TRUE;
sectnum=0;
for (;;) {
if (flen <= (charssent + 896L))
thisblklen = 128;
if ( !filbuf(txbuf, thisblklen))
break;
if (wcputsec(txbuf, ++sectnum, thisblklen)==ERROR)
return ERROR;
charssent += thisblklen;
}
fclose(in);
attempts=0;
do {
purgeline();
sendline(EOT);
fflush(stdout);
++attempts;
}
while ((firstch=(readline(Rxtimeout)) != ACK) && attempts < RETRYMAX);
if (attempts == RETRYMAX) {
zperr("No ACK on EOT");
return ERROR;
}
else
return OK;
}
/**
* @param cseclen :data length of this sector to send
*/
int wcputsec(char *buf, int sectnum, int cseclen)
{
register int checksum, wcj;
register char *cp;
unsigned oldcrc;
int firstch;
int attempts;
firstch=0; /* part of logic to detect CAN CAN */
if (Verbose>2)
fprintf(stderr, "Sector %3d %2dk\n", Totsecs, Totsecs/8 );
else if (Verbose>1)
fprintf(stderr, "\rSector %3d %2dk ", Totsecs, Totsecs/8 );
for (attempts=0; attempts <= RETRYMAX; attempts++) {
Lastrx= firstch;
sendline(cseclen==1024?STX:SOH);
sendline(sectnum);
sendline(-sectnum -1);
oldcrc=checksum=0;
for (wcj=cseclen,cp=buf; --wcj>=0; ) {
sendline(*cp);
oldcrc=updcrc((0377& *cp), oldcrc);
checksum += *cp++;
}
if (Crcflg) {
oldcrc=updcrc(0,updcrc(0,oldcrc));
sendline((int)oldcrc>>8);
sendline((int)oldcrc);
}
else
sendline(checksum);
if (Optiong) {
firstsec = FALSE; return OK;
}
firstch = readline(Rxtimeout);
gotnak:
switch (firstch) {
case CAN:
if(Lastrx == CAN) {
cancan:
zperr("Cancelled"); return ERROR;
}
break;
case TIMEOUT:
zperr("Timeout on sector ACK"); continue;
case WANTCRC:
if (firstsec)
Crcflg = TRUE;
case NAK:
zperr("NAK on sector"); continue;
case ACK:
firstsec=FALSE;
Totsecs += (cseclen>>7);
return OK;
case ERROR:
zperr("Got burst for sector ACK"); break;
default:
zperr("Got %02x for sector ACK", firstch); break;
}
for (;;) {
Lastrx = firstch;
if ((firstch = readline(Rxtimeout)) == TIMEOUT)
break;
if (firstch == NAK || firstch == WANTCRC)
goto gotnak;
if (firstch == CAN && Lastrx == CAN)
goto cancan;
}
}
zperr("Retry Count Exceeded");
return ERROR;
}
/* fill buf with count chars padding with ^Z for CPM */
int filbuf(char *buf, int count)
{
register int c, m;
if ( !Ascii) {
m = read(fileno(in), buf, count);
if (m <= 0)
return 0;
while (m < count)
buf[m++] = 032;
return count;
}
m=count;
if (Lfseen) {
*buf++ = 012; --m; Lfseen = 0;
}
while ((c=getc(in))!=EOF) {
if (c == 012) {
*buf++ = 015;
if (--m == 0) {
Lfseen = TRUE; break;
}
}
*buf++ =c;
if (--m == 0)
break;
}
if (m==count)
return 0;
else
while (--m>=0)
*buf++ = CPMEOF;
return count;
}
/* Fill buffer with blklen chars */
int zfilbuf()
{
int n;
#ifdef TXBSIZE
/* We assume request is within buffer, or just beyond */
txbuf = Txb + (bytcnt & TXBMASK);
if (vpos <= bytcnt) {
n = fread(txbuf, 1, blklen, in);
vpos += n;
if (n < blklen)
Eofseen = 1;
return n;
}
if (vpos >= (bytcnt+blklen))
return blklen;
/* May be a short block if crash recovery etc. */
Eofseen = BEofseen;
return (vpos - bytcnt);
#else
n = fread(txbuf, 1, blklen, in);
if (n < blklen)
Eofseen = 1;
return n;
#endif
}
#ifdef TXBSIZE
int fooseek(FILE *fptr, long pos, int whence)
{
int m, n;
vfile("fooseek: pos =%lu vpos=%lu Canseek=%d", pos, vpos, Canseek);
/* Seek offset < current buffer */
if (pos < (vpos -TXBSIZE +1024)) {
BEofseen = 0;
if (Canseek > 0) {
vpos = pos & ~TXBMASK;
if (vpos >= pos)
vpos -= TXBSIZE;
if (fseek(fptr, vpos, 0))
return 1;
}
else if (Canseek == 0)
if (fseek(fptr, vpos = 0L, 0))
return 1;
else
return 1;
while (vpos <= pos) {
n = fread(Txb, 1, TXBSIZE, fptr);
vpos += n;
vfile("n=%d vpos=%ld", n, vpos);
if (n < TXBSIZE) {
BEofseen = 1;
break;
}
}
vfile("vpos=%ld", vpos);
return 0;
}
/* Seek offset > current buffer (crash recovery, etc.) */
if (pos > vpos) {
if (Canseek)
if (fseek(fptr, vpos = (pos & ~TXBMASK), 0))
return 1;
while (vpos <= pos) {
txbuf = Txb + (vpos & TXBMASK);
m = TXBSIZE - (vpos & TXBMASK);
n = fread(txbuf, 1, m, fptr);
vpos += n;
vfile("bo=%d n=%d vpos=%ld", txbuf-Txb, n, vpos);
if (m < n) {
BEofseen = 1;
break;
}
}
return 0;
}
/* Seek offset is within current buffer */
vfile("vpos=%ld", vpos);
return 0;
}
#define fseek fooseek
#endif
void vfile(const char *string, ...)
{
if (Verbose > 2) {
va_list args;
va_start(args, string);
vfprintf(stderr, string, args);
va_end(args);
fprintf(stderr, "\n");
}
}
void alrm(int sig)
{
longjmp(tohere, -1);
}
#ifndef vax11c
/*
* readline(timeout) reads character(s) from file descriptor 0
* timeout is in tenths of seconds
*/
int readline(int timeout)
{
register int c;
static char byt[1];
fflush(stdout);
if (setjmp(tohere)) {
zperr("TIMEOUT");
return TIMEOUT;
}
c = timeout/10;
if (c<2)
c=2;
if (Verbose>5) {
fprintf(stderr, "Timeout=%d Calling alarm(%d) ", timeout, c);
}
signal(SIGALRM, alrm); alarm(c);
c=read(iofd, byt, 1);
alarm(0);
if (Verbose>5)
fprintf(stderr, "ret %x\n", byt[0]);
if (c<1)
return TIMEOUT;
return (byt[0]&0377);
}
void flushmo()
{
fflush(stdout);
}
void purgeline()
{
#ifdef USG
ioctl(iofd, TCFLSH, 0);
#else
lseek(iofd, 0L, 2);
#endif
}
#endif
/* send cancel string to get the other end to shut up */
void 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
};
#ifdef vax11c
raw_wbuf(strlen(canistr), canistr);
purgeline();
#else
printf(canistr);
fflush(stdout);
#endif
}
/*
* Log an error
*/
/*VARARGS1*/
void zperr(char *s, char *p, char *u)
{
if (Verbose <= 0)
return;
fprintf(stderr, "\nRetry %d: ", errors);
fprintf(stderr, s, p, u);
fprintf(stderr, "\n");
}
/*
* substr(string, token) searches for token in string s
* returns pointer to token within string if found, NULL otherwise
*/
char *
substr(char *s, char *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 (char *)NULL;
}
char *babble[] = {
#ifdef vax11c
" Send file(s) with ZMODEM Protocol",
"Usage: sz [-2+abdefkLlNnquvwYy] [-] file ...",
" sz [-2Ceqv] -c COMMAND",
" \\ Force next option letter to upper case",
#else
"Send file(s) with ZMODEM/YMODEM/XMODEM Protocol",
" (Y) = Option applies to YMODEM only",
" (Z) = Option applies to ZMODEM only",
"Usage: sz [-2+abdefkLlNnquvwYy] [-] file ...",
" sz [-2Ceqv] -c COMMAND",
" sb [-2adfkquv] [-] file ...",
" sx [-2akquv] [-] file",
#endif
#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)",
#ifndef vax11c
" d Change '.' to '/' in pathnames (Y/Z)",
#endif
" 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 (Z)",
" N send file if source newer or longer (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)",
#ifndef vax11c
" u Unlink file after transmission",
#endif
" v Verbose - provide debugging information",
" w N Window is N bytes (Z)",
" Y Yes, overwrite existing file, skip if not present at rx (Z)",
" y Yes, overwrite existing file (Z)",
"- as pathname sends standard input as sPID.sz or environment ONAME",
""
};
int usage()
{
char **pp;
for (pp=babble; **pp; ++pp)
fprintf(stderr, "%s\n", *pp);
fprintf(stderr, "%s for %s by Chuck Forsberg, Omen Technology INC\n",
VERSION, OS);
fprintf(stderr, "\t\t\042The High Reliability Software\042\n");
cucheck();
exit(SS_NORMAL);
}
/*
* Get the receiver's init parameters
*/
int getzrxinit()
{
register int 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));
Zctlesc |= Rxflags & TESCCTL;
Rxbuflen = (0377 & Rxhdr[ZP0])+((0377 & Rxhdr[ZP1])<<8);
if ( !(Rxflags & CANFDX))
Txwindow = 0;
vfile("Rxbuflen=%d Tframlen=%d", Rxbuflen, Tframlen);
if ( !Fromcu)
signal(SIGINT, SIG_IGN);
#ifdef MODE2OK
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);
#ifndef vax11c
/* If using a pipe for testing set lower buf len */
fstat(iofd, &f);
if ((f.st_mode & S_IFMT) != S_IFCHR) {
Rxbuflen = 1024;
}
#endif
#ifdef BADSEEK
Canseek = 0;
Txwindow = TXBSIZE - 1024;
Txwspac = TXBSIZE/4;
#endif
/*
* If input is not a regular file, force ACK's to
* prevent running beyond the buffer limits
*/
if ( !Command) {
fstat(fileno(in), &f);
if ((f.st_mode & S_IFMT) != S_IFREG) {
Canseek = -1;
#ifdef TXBSIZE
Txwindow = TXBSIZE - 1024;
Txwspac = TXBSIZE/4;
#else
return ERROR;
#endif
}
}
/* Set initial subpacket length */
if (blklen < 1024) { /* Command line override? */
if (Baudrate > 300)
blklen = 256;
if (Baudrate > 1200)
blklen = 512;
if (Baudrate > 2400)
blklen = 1024;
}
if (Rxbuflen && blklen>Rxbuflen)
blklen = Rxbuflen;
if (blkopt && blklen > blkopt)
blklen = blkopt;
vfile("Rxbuflen=%d blklen=%d", Rxbuflen, blklen);
vfile("Txwindow = %u Txwspac = %d", Txwindow, Txwspac);
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 */
int sendzsinit()
{
register int c;
if (Myattn[0] == '\0' && (!Zctlesc || (Rxflags & TESCCTL)))
return OK;
errors = 0;
for (;;) {
stohdr(0L);
if (Zctlesc) {
Txhdr[ZF0] |= TESCCTL; zshhdr(ZSINIT, Txhdr);
}
else
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 > 19)
return ERROR;
continue;
}
}
}
/* Send file name and related info */
int zsendfile(char *buf, int blen)
{
register int c;
register UNSL long crc;
for (;;) {
Txhdr[ZF0] = Lzconv; /* file conversion request */
Txhdr[ZF1] = Lzmanag; /* file management request */
if (Lskipnocor)
Txhdr[ZF1] |= ZMSKNOLOC;
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:
while ((c = readline(50)) > 0)
if (c == ZPAD) {
goto again;
}
/* **** FALL THRU TO **** */
default:
continue;
case ZCAN:
case TIMEOUT:
case ZABORT:
case ZFIN:
return ERROR;
case ZCRC:
crc = 0xFFFFFFFFL;
if (Canseek >= 0) {
while (((c = getc(in)) != EOF) && --Rxpos)
crc = UPDC32(c, crc);
crc = ~crc;
clearerr(in); /* Clear EOF */
fseek(in, 0L, 0);
}
stohdr(crc);
zsbhdr(ZCRC, Txhdr);
goto again;
case ZSKIP:
fclose(in); return c;
case ZRPOS:
/*
* Suppress zcrcw request otherwise triggered by
* lastyunc==bytcnt
*/
if (Rxpos && fseek(in, Rxpos, 0))
return ERROR;
Lastsync = (bytcnt = Txpos = Rxpos) -1;
return zsendfdata();
}
}
}
/* Send the data in the file */
int zsendfdata()
{
register int c, e, n;
register int newcnt;
register long tcount = 0;
int junkcount; /* Counts garbage chars received by TX */
static int tleft = 6; /* Counter for test mode */
Lrxpos = 0;
junkcount = 0;
Beenhereb4 = FALSE;
somemore:
if (setjmp(intrjmp)) {
waitack:
junkcount = 0;
c = getinsync(0);
gotack:
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 SV
switch (checked)
#else
switch (readline(1))
#endif
{
case CAN:
case ZPAD:
c = getinsync(1);
goto gotack;
case XOFF: /* Wait a while for an XON */
case XOFF|0200:
readline(100);
}
}
#endif
}
if ( !Fromcu)
signal(SIGINT, onintr);
newcnt = Rxbuflen;
Txwcnt = 0;
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 (Test) {
if ( --tleft)
while (tcount < 20000) {
printf(qbf); fflush(stdout);
tcount += strlen(qbf);
#ifdef READCHECK
while (rdchk(iofd)) {
#ifdef SV
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(SS_NORMAL);
}
do {
n = zfilbuf();
if (Eofseen)
e = ZCRCE;
else if (junkcount > 3)
e = ZCRCW;
else if (bytcnt == Lastsync)
e = ZCRCW;
else if (Rxbuflen && (newcnt -= n) <= 0)
e = ZCRCW;
else if (Txwindow && (Txwcnt += n) >= Txwspac) {
Txwcnt = 0; e = ZCRCQ;
}
else
e = ZCRCG;
if (Verbose>1)
fprintf(stderr, "\r%7ld ZMODEM%s ",
Txpos, Crc32t?" CRC-32":"");
zsdata(txbuf, n, e);
bytcnt = Txpos += n;
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 SV
switch (checked)
#else
switch (readline(1))
#endif
{
case CAN:
case ZPAD:
c = getinsync(1);
if (c == ZACK)
break;
#ifdef TCFLSH
ioctl(iofd, TCFLSH, 1);
#endif
/* zcrce - dinna wanna starta ping-pong game */
zsdata(txbuf, 0, ZCRCE);
goto gotack;
case XOFF: /* Wait a while for an XON */
case XOFF|0200:
readline(100);
default:
++junkcount;
}
}
#endif /* READCHECK */
if (Txwindow) {
while ((tcount = Txpos - Lrxpos) >= Txwindow) {
vfile("%ld window >= %u", tcount, Txwindow);
if (e != ZCRCQ)
zsdata(txbuf, 0, e = ZCRCQ);
c = getinsync(1);
if (c != ZACK) {
#ifdef TCFLSH
ioctl(iofd, TCFLSH, 1);
#endif
zsdata(txbuf, 0, ZCRCE);
goto gotack;
}
}
vfile("window = %ld", tcount);
}
} while (!Eofseen);
if ( !Fromcu)
signal(SIGINT, SIG_IGN);
for (;;) {
stohdr(Txpos);
zsbhdr(ZEOF, Txhdr);
switch (getinsync(0)) {
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
*/
int getinsync(int flag)
{
register int c;
for (;;) {
if (Test) {
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 sending to a buffered modem, you */
/* might send a break at this point to */
/* dump the modem's buffer. */
clearerr(in); /* In case file EOF seen */
if (fseek(in, Rxpos, 0))
return ERROR;
Eofseen = 0;
bytcnt = Lrxpos = Txpos = Rxpos;
if (Lastsync == Rxpos) {
if (++Beenhereb4 > 4)
if (blklen > 32)
blklen /= 2;
}
Lastsync = Rxpos;
return c;
case ZACK:
Lrxpos = Rxpos;
if (flag || Txpos == Rxpos)
return ZACK;
continue;
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 */
void saybibi()
{
for (;;) {
stohdr(0L); /* CAF Was zsbhdr - minor change */
zshhdr(ZFIN, Txhdr); /* to make debugging easier */
switch (zgethdr(Rxhdr, 0)) {
case ZFIN:
sendline('O'); sendline('O'); flushmo();
case ZCAN:
case TIMEOUT:
return;
}
}
}
/* Local screen character display function */
void bttyout(int c)
{
if (Verbose)
putc(c, stderr);
}
/* Send command and related info */
int zsendcmd(char *buf, int blen)
{
register int c;
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:
goto listen; /* CAF 8-21-87 */
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 > 20)
return ERROR;
continue;
case ZCOMPL:
Exitcode = Rxpos;
saybibi();
return OK;
case ZRQINIT:
#ifdef vax11c /* YAMP :== Yet Another Missing Primitive */
return ERROR;
#else
vfile("******** RZ *******");
system("rz");
vfile("******** SZ *******");
goto listen;
#endif
}
}
}
/*
* If called as sb use YMODEM protocol
*/
void chkinvok(char *s)
{
#ifdef vax11c
Progname = "sz";
#else
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=1024;
}
if (s[0]=='s' && s[1]=='x') {
Modem2 = TRUE;
}
#endif
}
void countem(int argc, char **argv)
{
register int c;
struct stat f;
for (Totalleft = 0, Filesleft = 0; --argc >=0; ++argv) {
f.st_size = -1;
if (Verbose>2) {
fprintf(stderr, "\nCountem: %03d %s ", argc, *argv);
fflush(stderr);
}
if (access(*argv, 04) >= 0 && stat(*argv, &f) >= 0) {
c = f.st_mode & S_IFMT;
if (c != S_IFDIR && c != S_IFBLK) {
++Filesleft; Totalleft += f.st_size;
}
}
if (Verbose>2)
fprintf(stderr, " %lld", f.st_size);
}
if (Verbose>2)
fprintf(stderr, "\ncountem: Total %d %ld\n",
Filesleft, Totalleft);
}
void chartest(int m)
{
register int n;
mode(m);
printf("\r\n\nCharacter Transparency Test Mode %d\r\n", m);
printf("If Pro-YAM/ZCOMM is not displaying ^M hit ALT-V NOW.\r\n");
printf("Hit Enter.\021"); fflush(stdout);
readline(500);
for (n = 0; n < 256; ++n) {
if (!(n%8))
printf("\r\n");
printf("%02x ", n); fflush(stdout);
sendline(n); flushmo();
printf(" "); fflush(stdout);
if (n == 127) {
printf("Hit Enter.\021"); fflush(stdout);
readline(500);
printf("\r\n"); fflush(stdout);
}
}
printf("\021\r\nEnter Characters, echo is in hex.\r\n");
printf("Hit SPACE or pause 40 seconds for exit.\r\n");
while (n != TIMEOUT && n != ' ') {
n = readline(400);
printf("%02x\r\n", n);
fflush(stdout);
}
printf("\r\nMode %d character transparency test ends.\r\n", m);
fflush(stdout);
}
/* End of sz.c */