#define VERSION "sz 1.26 03-10-87" #define PUBDIR "/usr/spool/uucppublic" /*% cc -O -K -i -DNFGVMIN -DREADCHECK sz.c -lx -o sz; size sz * (above for SYS III/V Xenix) * sz2.c By Chuck Forsberg * * cc -O sz?.c -o sz USG (SYS III/V) Unix * cc -O -DSVR2 sz.c -o sz Sys V Release 2 with non-blocking input * Define to allow reverse channel checking * cc -O -DV7 sz.c -o sz Unix Version 7, 2.8 - 4.3 BSD * * cc -O -K -i -DNFGVMIN -DREADCHECK sz.c -lx -o sz Xenix * * ln sz sb **** All versions **** * * * ******* Some systems (Venix, Coherent, Regulus) do not ******* * ******* support tty raw mode read(2) identically to ******* * ******* Unix. ONEREAD must be defined to force one ******* * ******* character reads for these systems. ******* * * A program for Unix to send files and commands to computers running * Professional-YAM, PowerCom, YAM, IMP, or programs supporting Y/XMODEM. * * Sz uses buffered I/O to greatly reduce CPU time compared to UMODEM. * * USG UNIX (3.0) ioctl conventions courtesy Jeff Martin */ extern char *substr(), *getenv(); #define LOGFILE "/tmp/szlog" #define zperr vfile #include #include #include #include #define PATHLEN 256 #define OK 0 #define FALSE 0 #define TRUE 1 #define ERROR (-1) #define HOWMANY 2 extern int Zmodem; /* ZMODEM protocol requested */ extern unsigned Baudrate; extern int Fromcu; /* Were called from cu or yam */ #include "bb.c" /* most of the system dependent stuff here */ #include "zmodem2.h" #define sendline(c) putchar(c & Wcsmask) extern int iofd, Twostop, Nozmodem; int onintr(); extern char *Progname; /* * Attention string to be executed by receiver to interrupt streaming data * when an error is detected. A pause (0336) may be needed before the * ^C (03) or after it. */ extern char Myattn[]; extern FILE *in; /* Ward Christensen / CP/M parameters - Don't change these! */ #define ENQ 005 #define CAN ('X'&037) #define XOFF ('s'&037) #define XON ('q'&037) #define SOH 1 #define STX 2 #define EOT 4 #define ACK 6 #define NAK 025 #define CPMEOF 032 #define WANTCRC 0103 /* send C not NAK to get crc not checksum */ #define WANTG 0107 /* Send G not NAK to get nonstop batch xmsn */ #define TIMEOUT (-2) #define RCDO (-3) #define RETRYMAX 10 #define SECSIZ 128 /* cp/m's Magic Number record size */ #define KSIZE 1024 extern char Lastrx; extern char Crcflg; extern int Wcsmask; extern int Verbose; extern int Modem; /* MODEM - don't send pathnames */ extern int Restricted; /* restricted; no /.. or ../ in filenames */ extern int Quiet; /* overrides logic that would otherwise set verbose */ extern int Ascii; /* Add CR's for brain damaged programs */ extern int Fullname; /* transmit full pathname */ extern int Unlinkafter; /* Unlink file after it is sent */ extern int Dottoslash; /* Change foo.bar.baz to foo/bar/baz */ extern int firstsec; extern int errcnt; /* number of files unreadable */ extern int blklen; /* length of transmitted records */ extern int Optiong; /* Let it rip no wait for sector ACK's */ extern int Noeofseen; extern int Totsecs; /* total number of sectors this file */ extern char txbuf[]; extern int Filcnt; /* count of number of files opened */ extern int Lfseen; extern unsigned Rxbuflen; /* Receiver's max buffer length */ extern int Tframlen; /* Override for tx frame length */ extern int blkopt; /* Override value for zmodem blklen */ extern int Rxflags; extern int Wantfcs32; /* want to send 32 bit FCS */ extern char Lzconv; /* Local ZMODEM file conversion request */ extern char Lzmanag; /* Local ZMODEM file management request */ extern char Lztrans; extern char zconv; /* ZMODEM file conversion request */ extern char zmanag; /* ZMODEM file management request */ extern char ztrans; /* ZMODEM file transport request */ extern int Command; /* Send a command, then exit. */ extern char *Cmdstr; /* Pointer to the command string */ extern int Cmdtries; extern int Cmdack1; /* Rx ACKs command, then do it */ extern int Exitcode; extern int Testattn; /* Force receiver to send Attn, etc with qbf. */ extern char *qbf; extern long Lastread; /* Beginning offset of last buffer read */ extern int Lastc; /* Count of last buffer read or -1 */ extern int Dontread; /* Don't read the buffer, it's still there */ extern jmp_buf tohere; /* For the interrupt on RX timeout */ extern jmp_buf intrjmp; /* For the interrupt on RX CAN */ extern int alrm(); /* * readock(timeout, count) reads character(s) from file descriptor 0 * (1 <= count <= 3) * it attempts to read count characters. If it gets more than one, * it is an error unless all are CAN * (otherwise, only normal response is ACK, CAN, or C) * Only looks for one if Optiong, which signifies cbreak, not raw input * * timeout is in tenths of seconds */ readock(timeout, count) { register int c; static char byt[5]; if (Optiong) count = 1; /* Special hack for cbreak */ fflush(stdout); if (setjmp(tohere)) { logent("TIMEOUT\n"); return TIMEOUT; } c = timeout/10; if (c<2) c=2; if (Verbose>3) { fprintf(stderr, "Timeout=%d Calling alarm(%d) ", timeout, c); byt[1] = 0; } signal(SIGALRM, alrm); alarm(c); #ifdef ONEREAD c=read(iofd, byt, 1); /* regulus raw read is unique */ #else c=read(iofd, byt, count); #endif alarm(0); if (Verbose>5) fprintf(stderr, "ret cnt=%d %x %x\n", c, byt[0], byt[1]); if (c<1) return TIMEOUT; if (c==1) return (byt[0]&0377); else while (c) if (byt[--c] != CAN) return ERROR; return CAN; } readline(n) { return (readock(n, 1)); } purgeline() { #ifdef USG ioctl(iofd, TCFLSH, 0); #else lseek(iofd, 0L, 2); #endif } /* send cancel string to get the other end to shut up */ canit() { static char canistr[] = { 24,24,24,24,24,24,24,24,24,24,8,8,8,8,8,8,8,8,8,8,0 }; printf(canistr); fflush(stdout); } /*VARARGS1*/ logent(a, b, c) char *a, *b, *c; { if(Verbose) fprintf(stderr, a, b, c); } /* * return 1 iff stdout and stderr are different devices * indicating this program operating with a modem on a * different line */ from_cu() { struct stat a, b; fstat(1, &a); fstat(2, &b); return (a.st_rdev != b.st_rdev); } /* * substr(string, token) searches for token in string s * returns pointer to token within string if found, NULL otherwise */ char * substr(s, t) register char *s,*t; { register char *ss,*tt; /* search for first char of token */ for (ss=s; *s; s++) if (*s == *t) /* compare token with substring */ for (ss=s,tt=t; ;) { if (*tt == 0) return s; if (*ss++ != *tt++) break; } return NULL; } char *babble[] = { "Send file(s) with ZMODEM/YMODEM/XMODEM Protocol", " (Y) = Option applies to YMODEM only", " (Z) = Option applies to ZMODEM only", "Usage: sz [-12+aBbdefkLlNnquvXy] [-] file ...", " sz [-1Beqv] -c COMMAND", " 1 Use stdout for modem input", #ifdef CSTOPB " 2 Use 2 stop bits", #endif " + Append to existing destination file (Z)", " a (ASCII) change NL to CR/LF", " b Binary file transfer override", " c send COMMAND (Z)", " d Change '.' to '/' in pathnames (Y/Z)", " e Escape all control characters (Z)", " f send Full pathname (Y/Z)", " i send COMMAND, ack Immediately (Z)", " k Send 1024 byte packets (Y)", " L N Limit subpacket length to N bytes (Z)", " l N Limit frame length to N bytes (l>=L) (Z)", " n send file if source Newer or longer (Z)", " N send file if source different length or date (Z)", " o Use 16 bit CRC instead of 32 bit CRC (Z)", " p Protect existing destination file (Z)", " r Resume/Recover interrupted file transfer (Z)", " q Quiet (no progress reports)", " u Unlink file after transmission", " v Verbose - debugging information", " X XMODEM protocol - send no pathnames", " y Yes, overwrite existing file (Z)", "- as pathname sends standard input as sPID.sz or environment ONAME", "" }; usage() { char **pp; for (pp=babble; **pp; ++pp) fprintf(stderr, "%s\n", *pp); fprintf(stderr, "%s for %s by Chuck Forsberg ", VERSION, OS); exit(1); } /* * Get the receiver's init parameters */ getzrxinit() { register n; struct stat f; for (n=10; --n>=0; ) { switch (zgethdr(Rxhdr, 1)) { case ZCHALLENGE: /* Echo receiver's challenge numbr */ stohdr(Rxpos); zshhdr(ZACK, Txhdr); continue; case ZCOMMAND: /* They didn't see out ZRQINIT */ stohdr(0L); zshhdr(ZRQINIT, Txhdr); continue; case ZRINIT: Rxflags = 0377 & Rxhdr[ZF0]; Txfcs32 = (Wantfcs32 && (Rxflags & CANFC32)); Rxbuflen = (0377 & Rxhdr[ZP0])+((0377 & Rxhdr[ZP1])<<8); vfile("Rxbuflen=%d Tframlen=%d", Rxbuflen, Tframlen); if ( !Fromcu) signal(SIGINT, SIG_IGN); #ifdef USG mode(2); /* Set cbreak, XON/XOFF, etc. */ #endif #ifndef READCHECK #ifndef USG /* Use 1024 byte frames if no sample/interrupt */ if (Rxbuflen < 32 || Rxbuflen > 1024) { Rxbuflen = 1024; vfile("Rxbuflen=%d", Rxbuflen); } #endif #endif /* Override to force shorter frame length */ if (Rxbuflen && (Rxbuflen>Tframlen) && (Tframlen>=32)) Rxbuflen = Tframlen; if ( !Rxbuflen && (Tframlen>=32) && (Tframlen<=1024)) Rxbuflen = Tframlen; vfile("Rxbuflen=%d", Rxbuflen); /* If using a pipe for testing set lower buf len */ fstat(iofd, &f); if ((f.st_mode & S_IFMT) != S_IFCHR && (Rxbuflen == 0 || Rxbuflen > 4096)) Rxbuflen = 4096; /* * If input is not a regular file, force ACK's each 1024 * (A smarter strategey could be used here ...) */ fstat(fileno(in), &f); if (((f.st_mode & S_IFMT) != S_IFREG) && (Rxbuflen == 0 || Rxbuflen > 1024)) Rxbuflen = 1024; vfile("Rxbuflen=%d", Rxbuflen); return (sendzsinit()); case ZCAN: case TIMEOUT: return ERROR; case ZRQINIT: if (Rxhdr[ZF0] == ZCOMMAND) continue; default: zshhdr(ZNAK, Txhdr); continue; } } return ERROR; } /* Send send-init information */ sendzsinit() { register c; register errors; if (Myattn[0] == '\0') return OK; errors = 0; for (;;) { stohdr(0L); zsbhdr(ZSINIT, Txhdr); zsdata(Myattn, 1+strlen(Myattn), ZCRCW); c = zgethdr(Rxhdr, 1); switch (c) { case ZCAN: return ERROR; case ZACK: return OK; default: if (++errors > 9) return ERROR; continue; } } } /* Send file name and related info */ zsendfile(buf, blen) char *buf; { register c; for (;;) { Txhdr[ZF0] = Lzconv; /* file conversion request */ Txhdr[ZF1] = Lzmanag; /* file management request */ Txhdr[ZF2] = Lztrans; /* file transport request */ Txhdr[ZF3] = 0; zsbhdr(ZFILE, Txhdr); zsdata(buf, blen, ZCRCW); again: c = zgethdr(Rxhdr, 1); switch (c) { case ZRINIT: goto again; case ZCAN: case TIMEOUT: case ZABORT: case ZFIN: return ERROR; case ZSKIP: fclose(in); return c; case ZRPOS: fseek(in, Rxpos, 0); Txpos = Rxpos; Lastc = -1; Dontread = FALSE; return zsendfdata(); default: continue; } } } /* Send the data in the file */ zsendfdata() { register c, e; register newcnt; register long tcount = 0; static int tleft = 6; /* Counter for test mode */ if (Baudrate > 300) blklen = 256; if (Baudrate > 2400) blklen = KSIZE; if (Rxbuflen && blklen>Rxbuflen) blklen = Rxbuflen; if (blkopt && blklen > blkopt) blklen = blkopt; vfile("Rxbuflen=%d blklen=%d", Rxbuflen, blklen); somemore: if (setjmp(intrjmp)) { waitack: c = getinsync(); switch (c) { default: case ZCAN: fclose(in); return ERROR; case ZSKIP: fclose(in); return c; case ZACK: case ZRPOS: break; case ZRINIT: return OK; } #ifdef READCHECK /* * If the reverse channel can be tested for data, * this logic may be used to detect error packets * sent by the receiver, in place of setjmp/longjmp * rdchk(fdes) returns non 0 if a character is available */ while (rdchk(iofd)) { #ifdef SVR2 switch (checked) #else switch (readline(1)) #endif { case CAN: case ZPAD: goto waitack; case XOFF: /* Wait a while for an XON */ case XOFF|0200: readline(100); } } #endif } if ( !Fromcu) signal(SIGINT, onintr); newcnt = Rxbuflen; stohdr(Txpos); zsbhdr(ZDATA, Txhdr); /* * Special testing mode. This should force receiver to Attn,ZRPOS * many times. Each time the signal should be caught, causing the * file to be started over from the beginning. */ if (Testattn) { if ( --tleft) while (tcount < 20000) { printf(qbf); fflush(stdout); tcount += strlen(qbf); #ifdef READCHECK while (rdchk(iofd)) { #ifdef SVR2 switch (checked) #else switch (readline(1)) #endif { case CAN: case ZPAD: #ifdef TCFLSH ioctl(iofd, TCFLSH, 1); #endif goto waitack; case XOFF: /* Wait for XON */ case XOFF|0200: readline(100); } } #endif } signal(SIGINT, SIG_IGN); canit(); sleep(3); purgeline(); mode(0); printf("\nsz: Tcount = %ld\n", tcount); if (tleft) { printf("ERROR: Interrupts Not Caught\n"); exit(1); } exit(0); } do { if (Dontread) { c = Lastc; } else { c = zfilbuf(txbuf, blklen); Lastread = Txpos; Lastc = c; } if (Verbose > 10) vfile("Dontread=%d c=%d", Dontread, c); Dontread = FALSE; if (c < blklen) e = ZCRCE; else if (Rxbuflen && (newcnt -= c) <= 0) e = ZCRCW; else e = ZCRCG; zsdata(txbuf, c, e); Txpos += c; if (e == ZCRCW) goto waitack; #ifdef READCHECK /* * If the reverse channel can be tested for data, * this logic may be used to detect error packets * sent by the receiver, in place of setjmp/longjmp * rdchk(fdes) returns non 0 if a character is available */ fflush(stdout); while (rdchk(iofd)) { #ifdef SVR2 switch (checked) #else switch (readline(1)) #endif { case CAN: case ZPAD: #ifdef TCFLSH ioctl(iofd, TCFLSH, 1); #endif /* zcrce - dinna wanna start a ping-pong game */ zsdata(txbuf, 0, ZCRCE); goto waitack; case XOFF: /* Wait a while for an XON */ case XOFF|0200: readline(100); } } #endif } while (c == blklen); if ( !Fromcu) signal(SIGINT, SIG_IGN); for (;;) { stohdr(Txpos); zsbhdr(ZEOF, Txhdr); switch (getinsync()) { case ZACK: continue; case ZRPOS: goto somemore; case ZRINIT: return OK; case ZSKIP: fclose(in); return c; default: fclose(in); return ERROR; } } } /* * Respond to receiver's complaint, get back in sync with receiver */ getinsync() { register c; for (;;) { if (Testattn) { printf("\r\n\n\n***** Signal Caught *****\r\n"); Rxpos = 0; c = ZRPOS; } else c = zgethdr(Rxhdr, 0); switch (c) { case ZCAN: case ZABORT: case ZFIN: case TIMEOUT: return ERROR; case ZRPOS: if (Lastc >= 0 && Lastread == Rxpos) { Dontread = TRUE; } else { clearerr(in); /* In case file EOF seen */ fseek(in, Rxpos, 0); } Txpos = Rxpos; return c; case ZACK: return c; case ZRINIT: case ZSKIP: fclose(in); return c; case ERROR: default: zsbhdr(ZNAK, Txhdr); continue; } } } /* Say "bibi" to the receiver, try to do it cleanly */ saybibi() { for (;;) { stohdr(0L); zsbhdr(ZFIN, Txhdr); switch (zgethdr(Rxhdr, 0)) { case ZFIN: sendline('O'); sendline('O'); flushmo(); case ZCAN: case TIMEOUT: return; } } } /* Local screen character display function */ bttyout(c) { if (Verbose) putc(c, stderr); } /* Send command and related info */ zsendcmd(buf, blen) char *buf; { register c, errors; long cmdnum; cmdnum = getpid(); errors = 0; for (;;) { stohdr(cmdnum); Txhdr[ZF0] = Cmdack1; zsbhdr(ZCOMMAND, Txhdr); zsdata(buf, blen, ZCRCW); listen: Rxtimeout = 100; /* Ten second wait for resp. */ c = zgethdr(Rxhdr, 1); switch (c) { case ZRINIT: continue; case ERROR: case TIMEOUT: if (++errors > Cmdtries) return ERROR; continue; case ZCAN: case ZABORT: case ZFIN: case ZSKIP: case ZRPOS: return ERROR; default: if (++errors > 10) return ERROR; continue; case ZCOMPL: Exitcode = Rxpos; saybibi(); return OK; case ZRQINIT: vfile("******** RZ *******"); system("rz"); vfile("******** SZ *******"); goto listen; } } } /* * If called as sb use YMODEM protocol */ chkinvok(s) char *s; { register char *p; p = s; while (*p == '-') s = ++p; while (*p) if (*p++ == '/') s = p; if (*s == 'v') { Verbose=1; ++s; } Progname = s; if (s[0]=='s' && s[1]=='b') { Nozmodem = TRUE; blklen=KSIZE; } }