#define VERSION "sz 3.07 2-02-90" #define PUBDIR "/usr/spool/uucppublic" /*% cc -compat -M2 -Ox -K -i -DTXBSIZE=16384 -DNFGVMIN -DREADCHECK sz.c -lx -o sz; size sz /*% cc -Zi -DXX -DNFGVMIN -DREADCHECK sz.c -lx -o xsz; size xsz <-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 BADSEEK 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 * * * This version implements numerous enhancements including ZMODEM * Run Length Encoding and variable length headers. These * features were not funded by the original Telenet development * contract. * * This software may be freely used for non commercial and * educational (didactic only) purposes. This software may also * be freely used to support file transfer operations to or from * licensed Omen Technology products. Any programs which use * part or all of this software must be provided in source form * with this notice intact except by written permission from Omen * Technology Incorporated. * * Use of this software for commercial or administrative purposes * except when exclusively limited to interfacing Omen Technology * products requires a per port license payment of $20.00 US per * port (less in quantity). Use of this code by inclusion, * decompilation, reverse engineering or any other means * constitutes agreement to these conditions and acceptance of * liability to license the materials and payment of reasonable * legal costs necessary to enforce this license agreement. * * * Omen Technology Inc FAX: 503-621-3745 * Post Office Box 4681 * Portland OR 97208 * * This code is made available in the hope it will be useful, * BUT WITHOUT ANY WARRANTY OF ANY KIND OR LIABILITY FOR ANY * DAMAGES OF ANY KIND. * * * 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 */ #ifdef XX #define XARGSFILE "args" long Thisflen; #endif char *substr(), *getenv(); #ifdef vax11c #define STATIC #define BADSEEK #define TXBSIZE 32768 /* Must be power of two, < MAXINT */ #include #include #define STAT #define LOGFILE "szlog.tmp" #include #include #include #include #include #define OS "VMS" #define ROPMODE "r" #define READCHECK #define BUFWRITE extern int errno; #define SS_NORMAL SS$_NORMAL #define xsendline(c) sendline(c) #ifndef PROGNAME #define PROGNAME "sz" #endif #else /* vax11c */ #ifdef GENIE #define STATIC static #define LOGFILE "szlog" #define BADSEEK #define TXBSIZE 32768 /* Must be power of two, < MAXINT */ #define OS "GEnie" #define SS_NORMAL 0 #include #include #include #include #include #include #include FILDES fdes; extern int errno; int Binfile; long Thisflen; #define sendline(c) putchar(c & 0377) #define xsendline(c) putchar(c) #else /* GENIE */ #define LOGFILE "/tmp/szlog" #define SS_NORMAL 0 #include #include #include #include #include extern int errno; #define STATIC #define sendline(c) putchar(c & 0377) #define xsendline(c) putchar(c) #endif #endif #define PATHLEN 256 #define OK 0 #define FALSE 0 #ifdef TRUE #undef TRUE #endif #define TRUE 1 #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 GCOUNT (-4) #define RETRYMAX 10 #define HOWMANY 2 STATIC int Zmodem=0; /* ZMODEM protocol requested by receiver */ unsigned Baudrate=4800; /* Default, set by first mode() call */ STATIC unsigned Effbaud = 4800; STATIC unsigned Txwindow; /* Control the size of the transmitted window */ STATIC unsigned Txwspac; /* Spacing between zcrcq requests */ STATIC unsigned Txwcnt; /* Counter used to space ack requests */ STATIC long Lrxpos; /* Receiver's last reported offset */ STATIC int errors; #ifdef vax11c #include "vrzsz.c" /* most of the system dependent stuff here */ #else #ifdef GENIE #include "genie.c" /* most of the system dependent stuff here */ #else #include "rbsb.c" /* most of the system dependent stuff here */ #ifdef XX #undef STAT #endif #endif #endif #include "crctab.c" STATIC int Filesleft; STATIC 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 STATIC char Myattn[] = { 0 }; #else #ifdef USG STATIC char Myattn[] = { 03, 0336, 0 }; #else #ifndef GENIE STATIC char Myattn[] = { 0 }; #endif #endif #endif FILE *in; #ifdef BADSEEK STATIC 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 STATIC int Canseek = 1; /* 1: Can seek 0: only rewind -1: neither (pipe) */ #endif #ifdef TXBSIZE #define TXBMASK (TXBSIZE-1) STATIC char Txb[TXBSIZE]; /* Circular buffer for file reads */ STATIC char *txbuf = Txb; /* Pointer to current file segment */ #else STATIC char txbuf[1024]; #endif STATIC long vpos = 0; /* Number of bytes read from file */ STATIC char Lastrx; STATIC char Crcflg; STATIC int Verbose=0; STATIC int Modem2=0; /* XMODEM Protocol - don't send pathnames */ STATIC int Restricted=0; /* restricted; no /.. or ../ in filenames */ STATIC int Quiet=0; /* overrides logic that would otherwise set verbose */ STATIC int Ascii=0; /* Add CR's for brain damaged programs */ STATIC int Fullname=0; /* transmit full pathname */ STATIC int Unlinkafter=0; /* Unlink file after it is sent */ STATIC int Dottoslash=0; /* Change foo.bar.baz to foo/bar/baz */ STATIC int firstsec; STATIC int errcnt=0; /* number of files unreadable */ STATIC int blklen=128; /* length of transmitted records */ STATIC int Optiong; /* Let it rip no wait for sector ACK's */ STATIC int Eofseen; /* EOF seen on input set by zfilbuf */ STATIC int BEofseen; /* EOF seen on input set by fooseek */ STATIC int Totsecs; /* total number of sectors this file */ STATIC int Filcnt=0; /* count of number of files opened */ STATIC int Lfseen=0; STATIC unsigned Rxbuflen = 16384; /* Receiver's max buffer length */ STATIC int Tframlen = 0; /* Override for tx frame length */ STATIC int blkopt=0; /* Override value for zmodem blklen */ STATIC int Rxflags = 0; STATIC long bytcnt; STATIC int Wantfcs32 = TRUE; /* want to send 32 bit FCS */ STATIC char Lzconv; /* Local ZMODEM file conversion request */ STATIC char Lzmanag; /* Local ZMODEM file management request */ STATIC int Lskipnocor; STATIC char Lztrans; STATIC int Command; /* Send a command, then exit. */ STATIC char *Cmdstr; /* Pointer to the command string */ STATIC int Cmdtries = 11; STATIC int Cmdack1; /* Rx ACKs command, then do it */ STATIC int Exitcode; STATIC int Test; /* 1= Force receiver to send Attn, etc with qbf. */ /* 2= Character transparency test */ STATIC char *qbf= "The quick brown fox jumped over the lazy dog's back 1234567890\r\n"; STATIC long Lastsync; /* Last offset to which we got a ZRPOS */ STATIC int Beenhereb4; /* How many times we've been ZRPOS'd same place */ STATIC jmp_buf tohere; /* For the interrupt on RX timeout */ STATIC jmp_buf intrjmp; /* For the interrupt on RX CAN */ #ifdef XARGSFILE char * mystrsave(s) char *s; { register char *p; char *malloc(); if (p = malloc(strlen(s)+1) ) { strcpy(p, s); return p; } fprintf(stderr, "No memory for mystrsave!\n"); exit(1); } /* Remove (presumably) terminating CR and/or LF from string */ uncrlf(s) register char *s; { for ( ; *s; ++s) switch (*s) { case '\r': case '\n': *s = 0; return; } } #endif /* called by signal interrupt or terminate to clean things up */ bibi(n) { canit(); fflush(stdout); mode(0); fprintf(stderr, "sz: caught signal %d; exiting\n", n); #ifndef GENIE if (n == SIGQUIT) abort(); #endif if (n == 99) fprintf(stderr, "mode(2) in rbsb.c not implemented!!\n"); cucheck(); exit(128+n); } /* Called when ZMODEM gets an interrupt (^X) */ onintr() { signal(SIGINT, SIG_IGN); longjmp(intrjmp, -1); } STATIC int Zctlesc; /* Encode control characters */ STATIC int Nozmodem = 0; /* If invoked as "sb" */ STATIC char *Progname = "sz"; STATIC int Zrwindow = 1400; /* RX window size (controls garbage count) */ #include "zm.c" #include "zmr.c" #ifdef XARGSFILE #define XARGSMAX 256 char *xargv[XARGSMAX+1]; #endif main(argc, argv) char *argv[]; { register char *cp; register 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(); #ifdef vax11c chkinvok(PROGNAME); #else chkinvok(argv[0]); #endif 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': if (Lzconv == ZCRESUM) Lzmanag = (Lzmanag & ZMMASK) | ZMCRC; Lzconv = ZCRESUM; break; 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(SS_NORMAL); } 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; case 'Z': case 'z': Lztrans = ZTRLE; break; default: usage(); } } } else if ( !npats && argc>0) { if (argv[0][0]) { npats=argc; patts=argv; } } } 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, NULL); } if (Fromcu && !Quiet) { if (Verbose == 0) Verbose = 2; } vfile("%s %s for %s\n", Progname, VERSION, OS); #ifdef XARGSFILE vfile("npats=%d *patts=%s", npats, *patts); if (npats == 1 && !strcmp(XARGSFILE, *patts)) { in = fopen(XARGSFILE, "r"); if (!in) { printf(stderr, "Can't open / control file!\n"); exit(2); } for (npats=0,argv=patts=xargv; npats>7); } #endif fprintf(stderr, "Give your local XMODEM receive command now.\r\n"); fflush(stderr); return OK; } zperr("Awaiting pathname nak for %s", *name?name:""); 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 (*name) { #ifdef XX if (Thisflen >= 0) { sprintf(p, "%u 0 0 0 %d %ld", Thisflen, Filesleft, Totalleft); Totalleft -= Thisflen; } #endif #ifdef GENIE else sprintf(p, "%d", fdes.current_file_size * 1260); vfile("%s open Binfile=%d size=%ld", name, Binfile, fdes.current_file_size * 1260); #endif #ifdef STAT #ifndef XX if (fstat(fileno(in), &f)!= -1) sprintf(p, "%lu %lo %o 0 %d %ld", f.st_size, f.st_mtime, f.st_mode, Filesleft, Totalleft); Totalleft -= f.st_size; #endif #endif } if (--Filesleft <= 0) Totalleft = 0; if (Totalleft < 0) Totalleft = 0; #ifdef STAT /* 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; } #endif if (Zmodem) return zsendfile(txbuf, 1+strlen(p)+(p-txbuf)); if (wcputsec(txbuf, 0, 128)==ERROR) return ERROR; return OK; } getnak() { register 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; } } wctx(flen) 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); flushmo(); ++attempts; } while ((firstch=(readline(Rxtimeout)) != ACK) && attempts < RETRYMAX); if (attempts == RETRYMAX) { zperr("No ACK on EOT"); return ERROR; } else return OK; } wcputsec(buf, sectnum, cseclen) char *buf; int sectnum; int cseclen; /* data length of this sector to send */ { register 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); flushmo(); 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 */ filbuf(buf, count) register char *buf; { register 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 */ zfilbuf() { int n; #ifdef TXBSIZE vfile("zfilbuf: bytcnt =%lu vpos=%lu blklen=%d", bytcnt, vpos, blklen); /* We assume request is within buffer, or just beyond */ txbuf = Txb + (bytcnt & TXBMASK); if (vpos <= bytcnt) { #ifdef GENIE if (Binfile) { long l, m; char *p; for (p=txbuf, n=0, l=blklen; l; l -= 128, p+= 128) { n += m = fgetb(p, 128, in); if (m == 0) break; } } else #endif n = fread(txbuf, 1, blklen, in); vpos += n; if (n < blklen) Eofseen = 1; vfile("zfilbuf: n=%d vpos=%lu Eofseen=%d", n, vpos, Eofseen); 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 /* Replacement for brain damaged fseek function. Returns 0==success */ fooseek(fptr, pos, whence) FILE *fptr; long pos; { long m, n; #ifdef GENIE long l, k; char *p; #endif 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) { #ifdef GENIE if (Binfile) { if (fseekb(fptr, vpos = 0L, 0)) return 1; } else #endif if (fseek(fptr, vpos = 0L, 0)) return 1; } else return 1; while (vpos < pos) { #ifdef GENIE if (Binfile) { for (p=Txb,n=0,l=TXBSIZE; l; l -= 128,p+= 128) { n += (k = fgetb(p, 128, fptr)); vfile("bsk1: l=%d k=%d", l, k); if (k == 0) break; } } else #endif 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); vfile("m=%ld vpos=%ld", m,vpos); #ifdef GENIE if (Binfile) { for (p=txbuf,n=0,l=m; l; l -= 128,p+= 128) { n += (k = fgetb(p, 128, fptr)); vfile("bsk2: l=%d k=%d n=%d", l, k, n); if (k == 0) break; } } else #endif n = fread(txbuf, 1, m, fptr); vfile("n=%ld vpos=%ld", n,vpos); vpos += n; vfile("bo=%d m=%ld vpos=%ld", txbuf-Txb,m,vpos); if (n < m) { BEofseen = 1; break; } } return 0; } /* Seek offset is within current buffer */ vfile("within buffer: vpos=%ld", vpos); return 0; } #define fseek fooseek #endif /* VARARGS1 */ vfile(f, a, b, c, d) long a, b, c, d; { if (Verbose > 2) { fprintf(stderr, f, a, b, c, d); fprintf(stderr, "\n"); } } alrm() { longjmp(tohere, -1); } #ifndef GENIE #ifndef vax11c /* * readline(timeout) reads character(s) from file descriptor 0 * timeout is in tenths of seconds */ readline(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(0, byt, 1); alarm(0); if (Verbose>5) fprintf(stderr, "ret %x\n", byt[0]); if (c<1) return TIMEOUT; return (byt[0]&0377); } flushmo() { fflush(stdout); } purgeline() { #ifdef USG ioctl(0, TCFLSH, 0); #else lseek(0, 0L, 2); #endif } #endif #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 }; #ifdef vax11c raw_wbuf(strlen(canistr), canistr); purgeline(); #else printf(canistr); fflush(stdout); #endif } /* * Log an error */ /*VARARGS1*/ zperr(s,p,u) char *s, *p, *u; { if (Verbose <= 0) return; fprintf(stderr, "Retry %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(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[] = { #ifdef vax11c "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", " \\ Force next option letter to upper case", " sb [-2adfkquv] [-] file ...", " sx [-2akquv] [-] file", #endif #ifndef vax11c "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 only if source newer (Z)", " N send file only 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 (remove) file after transmission", #endif " v Verbose - provide debugging information", " w N restrict Window to N bytes (Z)", " Y Yes, overwrite existing file, skip if not present at rx (Z)", " y Yes, overwrite existing file (Z)", " Z Activate ZMODEM compression(Z)", "" }; 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 */ getzrxinit() { register n; #ifdef STAT struct stat f; #endif for (n=10; --n>=0; ) { switch (zgethdr(Rxhdr, 1)) { case ZCHALLENGE: /* Echo receiver's challenge numbr */ stohdr(Rxpos); zshhdr(4, ZACK, Txhdr); continue; case ZCOMMAND: /* They didn't see out ZRQINIT */ stohdr(0L); zshhdr(4, ZRQINIT, Txhdr); continue; case ZRINIT: Rxflags = 0377 & Rxhdr[ZF0]; Usevhdrs = Rxhdr[ZF1] & CANVHDR; 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 #ifndef GENIE /* Use 1024 byte frames if no sample/interrupt */ if (Rxbuflen < 32 || Rxbuflen > 1024) { Rxbuflen = 1024; vfile("Rxbuflen=%d", Rxbuflen); } #endif #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 GENIE #ifndef vax11c #ifdef STAT /* If using a pipe for testing set lower buf len */ fstat(0, &f); if ((f.st_mode & S_IFMT) != S_IFCHR) { Rxbuflen = 1024; } #endif #endif #endif #ifdef BADSEEK #ifdef GENIE if (Txwindow == 0) { Txwspac = (Txwindow = 4096)/4; } #else if (Txwindow == 0) Txwindow = TXBSIZE - 1024; Txwspac = TXBSIZE/4; #endif Canseek = 0; #endif /* * If input is not a regular file, force ACK's to * prevent running beyond the buffer limits */ #ifdef STAT 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 } } #endif /* Set initial subpacket length */ if (blklen < 1024) { /* Command line override? */ if (Effbaud > 300) blklen = 256; if (Effbaud > 1200) blklen = 512; if (Effbaud > 2400) blklen = 1024; } if (Rxbuflen && blklen>Rxbuflen) blklen = Rxbuflen; if (blkopt && blklen > blkopt) blklen = blkopt; #ifdef GENIE blklen /= 128; blklen *= 128; if (blklen < 128) blklen = 128; #endif vfile("Rxbuflen=%d blklen=%d", Rxbuflen, blklen); vfile("Txwindow = %u Txwspac = %d", Txwindow, Txwspac); if (Lztrans == ZTRLE && (Rxflags & CANRLE)) Txfcs32 = 2; else Lztrans = 0; return (sendzsinit()); case ZCAN: case TIMEOUT: return ERROR; case ZRQINIT: if (Rxhdr[ZF0] == ZCOMMAND) continue; default: zshhdr(4, ZNAK, Txhdr); continue; } } return ERROR; } /* Send send-init information */ sendzsinit() { register c; if (Myattn[0] == '\0' && (!Zctlesc || (Rxflags & TESCCTL))) return OK; errors = 0; for (;;) { stohdr(0L); #ifdef ALTCANOFF Txhdr[ALTCOFF] = ALTCANOFF; #endif if (Zctlesc) { Txhdr[ZF0] |= TESCCTL; zshhdr(4, ZSINIT, Txhdr); } else zsbhdr(4, ZSINIT, Txhdr); zsdata(Myattn, ZATTNLEN, 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 */ zsendfile(buf, blen) char *buf; { register c; register UNSL long crc; long lastcrcrq = -1; char *p; 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(4, 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; } continue; case ZCAN: case TIMEOUT: case ZABORT: case ZFIN: default: return ERROR; case ZCRC: if (Rxpos != lastcrcrq) { lastcrcrq = Rxpos; crc = 0xFFFFFFFFL; if (Canseek >= 0) { fseek(in, 0L, 0); while (((c = getc(in)) != EOF) && --lastcrcrq) crc = UPDC32(c, crc); crc = ~crc; clearerr(in); /* Clear possible EOF */ lastcrcrq = Rxpos; } } stohdr(crc); zsbhdr(4, ZCRC, Txhdr); goto again; case ZFERR: case ZSKIP: fclose(in); return c; case ZRPOS: /* * Suppress zcrcw request otherwise triggered by * lastyunc==bytcnt */ #ifdef GENIE /* * Special case - turn on RLE if not archive, etc. * otherwise turn off RLE unless cmd line specified */ if (Rxflags & CANRLE) { /* RX can do it */ bytcnt = 0; zfilbuf(); vfile("txbuf012: %x %x %x", txbuf[0], txbuf[1], txbuf[2]); if ((txbuf[0] != 032) /* .ARC file */ && (txbuf[0] != 0x1f) /* .Z file */ && (txbuf[0] != 0x1c) /* .LHZ file */ && strncmp(txbuf, "ZOO", 3) && strncmp(txbuf, "GIF", 3) && (txbuf[2] != 3)) /* .ZIP file */ Txfcs32 = 2; else if ( !(Lztrans & ZTRLE)) Txfcs32 = 1; } /* GEnie binary can't seek to byte */ if (Binfile) { Rxpos &= ~127L; } #endif if (fseek(in, Rxpos, 0)) return ERROR; Lastsync = (bytcnt = Txpos = Lrxpos = Rxpos) -1; return zsendfdata(); } } } /* Send the data in the file */ zsendfdata() { register c, e, n; register newcnt; register long tcount = 0; int junkcount; /* Counts garbage chars received by TX */ static int tleft = 6; /* Counter for test mode */ 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(fd) returns non 0 if a character is available */ while (rdchk(0)) { #ifdef EATSIT 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(4, 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(0)) { #ifdef EATSIT switch (checked) #else switch (readline(1)) #endif { case CAN: case ZPAD: #ifdef TCFLSH ioctl(0, 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(fd) returns non 0 if a character is available */ fflush(stdout); while (rdchk(0)) { #ifdef EATSIT switch (checked) #else switch (readline(1)) #endif { case CAN: case ZPAD: c = getinsync(1); if (c == ZACK) break; #ifdef TCFLSH ioctl(0, 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(0, 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(4, 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 */ getinsync(flag) { register 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; #ifndef GENIE if (Lastsync == Rxpos) { if (++Beenhereb4 > 4) if (blklen > 32) blklen /= 2; } #endif 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(4, ZNAK, Txhdr); continue; } } } /* Say "bibi" to the receiver, try to do it cleanly */ saybibi() { for (;;) { stohdr(0L); /* CAF Was zsbhdr - minor change */ zshhdr(4, 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 */ bttyout(c) { if (Verbose) putc(c, stderr); } /* Send command and related info */ zsendcmd(buf, blen) char *buf; { register c; long cmdnum; #ifdef GENIE cmdnum = 69; #else cmdnum = getpid(); #endif errors = 0; for (;;) { stohdr(cmdnum); Txhdr[ZF0] = Cmdack1; zsbhdr(4, ZCOMMAND, Txhdr); zsdata(buf, blen, ZCRCW); listen: Rxtimeout = 100; /* Ten second wait for resp. */ Usevhdrs = 0; /* Allow rx to send fixed len headers */ c = zgethdr(Rxhdr, 1); switch (c) { case ZRINIT: goto listen; /* CAF 8-21-87 */ case ERROR: case GCOUNT: 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 */ 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=1024; } if (s[0]=='s' && s[1]=='x') { Modem2 = TRUE; } } #ifdef STAT countem(argc, argv) register char **argv; { register 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, " %ld", f.st_size); } if (Verbose>2) fprintf(stderr, "\ncountem: Total %d %ld\n", Filesleft, Totalleft); } #else countem(argc, argv) register char **argv; { register c; register char *p; long size; for (Totalleft = 0, Filesleft = 0; --argc >=0; ++argv) { size = -1; if (Verbose>2) { fprintf(stderr, "\nCountem: %03d %s ", argc, *argv); fflush(stderr); } ++Filesleft; #ifdef XARGSFILE /* Look for file length in third colon sep field */ for (p = *argv; *p; ++p) { if (*p == ':') { for (++p; *p; ++p) { if (*p == ':') { ++p; size = atol(p); Totalleft += size; break; } } break; } } #endif if (Verbose>2) fprintf(stderr, " %ld", size); } if (Verbose>2) fprintf(stderr, "\ncountem: Total %d %ld\n", Filesleft, Totalleft); } #endif chartest(m) { register 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 */