home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The C Users' Group Library 1994 August
/
wc-cdrom-cusersgrouplibrary-1994-08.iso
/
vol_200
/
260_01
/
rz.c
< prev
next >
Wrap
Text File
|
1988-02-25
|
34KB
|
1,413 lines
#define VERSION "1.14 01-15-87"
#define PUBDIR "/usr/spool/uucppublic"
/*% cc -DNFGVMIN -DCRCTABLE -K -O -i % -o rz; size rz
*
* rz.c By Chuck Forsberg
* MS-DOS version by Francois Bergeon
*
* cc -O rz.c -o rz USG (3.0) Unix
* cc -O -DV7 rz.c -o rz Unix V7, BSD 2.8 - 4.3
* cl rz.c -link stty MS-DOS, MS-C 4.00+
*
* ln rz rb For USG or V7
*
* ln rz /usr/bin/rzrmail For remote mail. Make this the
* login shell. rzrmail then calls
* rmail(1) to deliver mail.
*
* define CRCTABLE to use table driven CRC
*
* Unix is a trademark of Western Electric Company
*
* A program for Unix to receive files and commands from computers running
* Professional-YAM, PowerCom, YAM, IMP, or programs supporting XMODEM.
* rz uses Unix buffered input to reduce wasted CPU time.
*
* Iff the program is invoked by rzCOMMAND, output is piped to
* "COMMAND filename"
*
* Some systems (Venix, Coherent, Regulus) may not support tty raw mode
* read(2) the same way as Unix. ONEREAD must be defined to force one
* character reads for these systems. Added 7-01-84 CAF
*
* Alarm signal handling changed to work with 4.2 BSD 7-15-84 CAF
*
* NFGVMIN Added 1-13-85 CAF for PC-AT Xenix systems where c_cc[VMIN]
* doesn't seem to work (even though it compiles without error!).
*
* HOWMANY should be tuned for best performance
*
* USG UNIX (3.0) ioctl conventions courtesy Jeff Martin
*
* MS-DOS Adaptation 10-19-87 for the MS-C compiler rev 4.00+
* using Francois Bergeon's tty library
*/
#define LOGFILE "/tmp/rzlog"
#include <stdio.h>
#include <signal.h>
#include <setjmp.h>
#include <ctype.h>
#ifdef MSDOS
#include <time.h>
#define CRCTABLE
#define JAN1ST70 315532800
#else
FILE *popen();
#endif
#define OK 0
#define FALSE 0
#define TRUE 1
#define ERROR (-1)
/*
* Max value for HOWMANY is 255.
* A larger value reduces system overhead but may evoke kernel bugs.
* 133 corresponds to a XMODEM/CRC sector
*/
#ifndef HOWMANY
#define HOWMANY 133
#endif
int Zmodem = 0; /* ZMODEM protocol requested */
int Nozmodem = 0; /* If invoked as "rb" */
unsigned Baudrate;
#include "rbsb.c" /* most of the system dependent stuff here */
char *substr();
FILE *fout;
/*
* Routine to calculate the free bytes on the current file system
* ~0 means many free bytes (unknown)
*/
long getfree()
{
return(~0L); /* many free bytes ... */
}
/* 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 'C' /* send C not NAK to get crc not checksum */
#define TIMEOUT (-2)
#define RCDO (-3)
#define ERRORMAX 5
#define RETRYMAX 5
#define WCEOT (-10)
#define SECSIZ 128 /* cp/m's Magic Number record size */
#define PATHLEN 257 /* ready for 4.2 bsd ? */
#define KSIZE 1024 /* record size with k option */
#define UNIXFILE 0x8000 /* happens to the the S_IFREG file mask bit for stat */
int Lastrx;
int Crcflg = FALSE;
int Firstsec;
int Eofseen; /* indicates cpm eof (^Z) has been received */
int errors;
int Restricted = 0; /* restricted; no /.. or ../ in filenames */
#ifdef ONEREAD /* Sorry, Regulus and some others don't */
int Readnum = 1; /* work right in raw mode! */
#else
int Readnum = HOWMANY; /* Number of bytes to ask for in read() from modem */
#endif
#define DEFBYTL 2000000000L /* default rx file size */
long Bytesleft; /* number of bytes of incoming file left */
long Modtime; /* Unix style mod time for incoming file */
short Filemode; /* Unix style mode for incoming file */
char Pathname[PATHLEN];
char *Progname; /* the name by which we were called */
int Batch = 0;
int Wcsmask = 0377;
int Topipe = 0;
int MakeLCPathname = TRUE; /* make received pathname lower case */
int Verbose = 0;
int Quiet = 0; /* overrides logic that would otherwise set verbose */
int Nflag = 0; /* Don't really transfer files */
int Rxbinary = FALSE; /* receive all files in bin mode */
int Rxascii = FALSE; /* receive files in ascii (translate) mode */
int Thisbinary; /* current file is to be received in bin mode */
int Blklen; /* record length of received packets */
char secbuf[KSIZE];
char linbuf[HOWMANY];
int Lleft = 0; /* number of characters in linbuf */
time_t timep[2];
char Lzmanag; /* Local file management request */
char zconv; /* ZMODEM file conversion request */
char zmanag; /* ZMODEM file management request */
char ztrans; /* ZMODEM file transport request */
jmp_buf tohere; /* For the interrupt on RX timeout */
#include "zm.c"
int tryzhdrtype = ZRINIT; /* Header type to send corresponding to */
/* Last rx close */
alrm()
{
longjmp(tohere, -1);
}
/* called by signal interrupt or terminate to clean things up */
bibi(n)
{
if (Zmodem)
zmputs(Attn);
canit();
mode(0);
fprintf(stderr, "rz: caught signal %d; exiting\n", n);
exit(128+n);
}
main(argc, argv)
char *argv[];
{
char *cp;
register npats;
char *virgin, **patts;
char *getenv();
int exitcode;
Rxtimeout = 100;
setbuf(stderr, NULL);
if ((cp = getenv("SHELL")) && (substr(cp, "rsh") || substr(cp, "rksh")))
Restricted = TRUE;
#ifndef MSDOS
chkinvok((virgin = argv[0])); /* if called as [-]rzCOMMAND set flag */
#else
Progname = virgin = "rz";
#endif
npats = 0;
while (--argc)
{
cp = *++argv;
if (*cp++ == '-' && *cp)
{
while (*cp)
{
switch (*cp++)
{
case '+':
Lzmanag = ZMAPND;
break;
#ifndef MSDOS
case '1':
iofd = 1;
break;
#else
case '2':
port = "COM2";
break;
case 's':
Baudrate = atoi(cp);
if ((speed = getspeed(Baudrate)) < 0)
usage();
*cp = '\0';
break;
case 'Y':
Nozmodem = TRUE;
break;
#endif
case '7':
Wcsmask = 0177;
case 'a':
Rxascii = TRUE;
break;
case 'b':
Rxbinary = TRUE;
break;
case 'c':
Crcflg = TRUE;
break;
case 'D':
Nflag = TRUE;
break;
case 'p':
Lzmanag = ZMPROT;
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 'u':
MakeLCPathname = FALSE;
break;
case 'v':
++Verbose;
break;
default:
usage();
}
}
}
else if (!npats && argc > 0)
{
if (argv[0][0])
{
npats = argc;
patts = argv;
#ifdef MSDOS
if (!strcmp(*patts, "-"))
Topipe = TRUE;
#endif
}
}
}
if (npats > 1)
usage();
if (Verbose)
{
if (freopen(LOGFILE, "a", stderr) == NULL)
{
printf("Can't open log file %s\n",LOGFILE);
exit(0200);
}
setbuf(stderr, NULL);
fprintf(stderr, "argv[0]=%s Progname=%s\n", virgin, Progname);
}
if (fromcu() && !Quiet)
if (!Verbose)
Verbose = 2;
mode(1);
if (signal(SIGINT, bibi) == SIG_IGN)
{
signal(SIGINT, SIG_IGN);
#ifndef MSDOS
signal(SIGKILL, SIG_IGN);
#endif
}
else
{
signal(SIGINT, bibi);
#ifndef MSDOS
signal(SIGKILL, bibi);
#endif
}
if (wcreceive(npats, patts) == ERROR)
{
exitcode=0200;
canit();
}
mode(0);
if (exitcode && !Zmodem) /* bellow again with all thy might. */
canit();
exit(exitcode);
}
usage()
{
fprintf(stderr,"%s %s for %s by Chuck Forsberg\n", Progname, VERSION, OS);
#ifndef MSDOS
fprintf(stderr,"Usage: rz [-1abuv] (ZMODEM Batch)\n");
fprintf(stderr,"or rb [-1abuv] (YMODEM Batch)\n");
fprintf(stderr,"or rz [-1abcv] file (XMODEM or XMODEM-1k)\n");
fprintf(stderr," -1 For cu(1): Use fd 1 for input\n");
#else
fprintf(stderr,"Usage: rz [-2abuv] [-s<speed>] [-] (ZMODEM Batch)\n");
fprintf(stderr,"or rz [-2abuv] [-s<speed>] [-] -Y (YMODEM Batch)\n");
fprintf(stderr,"or rz [-2abcv] [-s<speed>] file (XMODEM)\n");
fprintf(stderr," -2 Use COM2 instead of COM1\n");
fprintf(stderr," - Pipe output to stdout\n");
#endif
fprintf(stderr," -a ASCII transfer (strip CR)\n");
fprintf(stderr," -b Binary transfer for all files\n");
fprintf(stderr," -v Verbose more v's give more info\n");
fprintf(stderr," -c Use 16 bit CRC (XMODEM)\n");
exit(1);
}
/*
* Debugging information output interface routine
*/
/* VARARGS1 */
vfile(f, a, b, c)
char *f;
{
if (Verbose > 2)
{
fprintf(stderr, f, a, b, c);
fprintf(stderr, "\n");
}
}
zperr(f, a, b, c)
char *f;
{
if (Verbose > 1)
{
fprintf(stderr, f, a, b, c);
fprintf(stderr, "\n");
}
}
/*
* Let's receive something already.
*/
char *rbmsg =
"%s ready. To begin transfer, type \"%s file ...\" to your modem program\r\n";
wcreceive(argc, argp)
char **argp;
{
int c;
if (Batch || argc == 0)
{
Crcflg = (Wcsmask == 0377);
if (!Quiet)
fprintf(stderr, rbmsg, Progname, Nozmodem ? "sb" : "sz");
if (c = tryz())
{
if (c == ZCOMPL)
return(OK);
if (c == ERROR)
goto fubar;
c = rzfiles();
if (c)
goto fubar;
}
else
{
for (;;)
{
if (wcrxpn(secbuf) == ERROR)
goto fubar;
if (secbuf[0] == 0)
return(OK);
if (procheader(secbuf) == ERROR)
goto fubar;
if (wcrx() == ERROR)
goto fubar;
}
}
}
else
{
Bytesleft = DEFBYTL;
Filemode = 0;
Modtime = 0L;
strcpy(Pathname, *argp);
checkpath(Pathname);
fprintf(stderr, "\nrz: ready to receive %s\r\n", Pathname);
#ifndef MSDOS
if ((fout = fopen(Pathname, "w")) == NULL)
#else
if ((fout = fopen(Pathname, "wb")) == NULL)
#endif
return(ERROR);
Thisbinary = Rxbinary || !Rxascii;
if (wcrx() == ERROR)
goto fubar;
}
return(OK);
fubar:
canit();
if (Topipe && fout)
{
#ifndef MSDOS
pclose(fout);
#endif
return(ERROR);
}
if (fout)
fclose(fout);
if (Restricted)
{
unlink(Pathname);
fprintf(stderr, "\r\nrz: %s removed.\r\n", Pathname);
}
return(ERROR);
}
/*
* Fetch a pathname from the other end as a C ctyle ASCIZ string.
* Length is indeterminate as long as less than Blklen
* A null string represents no more files (YMODEM)
*/
wcrxpn(rpn)
char *rpn; /* receive a pathname */
{
register c;
#ifdef NFGVMIN
readline(1);
#else
purgeline();
#endif
et_tu:
Firstsec = TRUE;
Eofseen = FALSE;
sendline(Crcflg ? WANTCRC : NAK);
Lleft = 0; /* Do read next time ... */
while ((c = wcgetsec(rpn, 100)) != 0)
{
log("Pathname fetch returned %d\n", c);
if (c == WCEOT)
{
sendline(ACK);
Lleft = 0; /* Do read next time ... */
readline(1);
goto et_tu;
}
return(ERROR);
}
sendline(ACK);
return(OK);
}
/*
* Adapted from CMODEM13.C, written by
* Jack M. Wierda and Roderick W. Hart
*/
wcrx()
{
register int sectnum, sectcurr;
register char sendchar;
register char *p;
int cblklen; /* bytes to dump this block */
Firstsec = TRUE;
sectnum = 0;
Eofseen = FALSE;
sendchar = (Crcflg ? WANTCRC : NAK);
for (;;)
{
sendline(sendchar); /* send it now, we're ready! */
Lleft = 0; /* Do read next time ... */
sectcurr = wcgetsec(secbuf, 100);
report(sectcurr);
if (sectcurr == (sectnum+1 & Wcsmask))
{
sectnum++;
cblklen = ((Bytesleft > Blklen) ? Blklen : Bytesleft);
if (putsec(secbuf, cblklen) == ERROR)
return(ERROR);
if ((Bytesleft -= cblklen) < 0)
Bytesleft = 0;
sendchar = ACK;
}
else if (sectcurr == (sectnum & Wcsmask))
{
log("Received dup Sector\n");
sendchar = ACK;
}
else if (sectcurr == WCEOT)
{
if (closeit())
return(ERROR);
sendline(ACK);
Lleft = 0; /* Do read next time ... */
return(OK);
}
else if (sectcurr == ERROR)
return(ERROR);
else
{
log("Sync Error\n");
return(ERROR);
}
}
}
/*
* Wcgetsec fetches a Ward Christensen type sector.
* Returns sector number encountered or ERROR if valid sector not received,
* or CAN CAN received
* or WCEOT if eot sector
* time is timeout for first char, set to 4 seconds thereafter
***************** NO ACK IS SENT IF SECTOR IS RECEIVED OK **************
* (Caller must do that when he is good and ready to get next sector)
*/
wcgetsec(rxbuf, maxtime)
char *rxbuf;
int maxtime;
{
int checksum, wcj, firstch;
unsigned short oldcrc;
char *p;
int sectcurr;
for (Lastrx = errors = 0; errors < RETRYMAX; errors++)
{
if ((firstch = readline(maxtime)) == STX)
{
Blklen = KSIZE;
goto get2;
}
if (firstch == SOH)
{
Blklen = SECSIZ;
get2:
sectcurr = readline(1);
if ((sectcurr + (oldcrc = readline(1))) == Wcsmask)
{
oldcrc = checksum = 0;
for (p = rxbuf, wcj = Blklen; --wcj >= 0; )
{
if ((firstch = readline(1)) < 0)
goto bilge;
oldcrc = updcrc(firstch, oldcrc);
checksum += firstch;
*p++ = firstch;
}
if ((firstch = readline(1)) < 0)
goto bilge;
if (Crcflg)
{
oldcrc = updcrc(firstch, oldcrc);
if ((firstch = readline(1)) < 0)
goto bilge;
oldcrc = updcrc(firstch, oldcrc);
if (oldcrc & 0xFFFF)
log("CRC=0%o\n", oldcrc);
else
{
Firstsec = FALSE;
return(sectcurr);
}
}
else if (((checksum - firstch) & Wcsmask) == 0)
{
Firstsec = FALSE;
return(sectcurr);
}
else
log("Checksum Error\n");
}
else
log("Sector number garbled 0%o 0%o\n", sectcurr, oldcrc);
}
/* make sure eot really is eot and not just mixmash */
#ifdef NFGVMIN
else if (firstch == EOT && readline(1) == TIMEOUT)
return(WCEOT);
#else
else if (firstch == EOT && Lleft == 0)
return(WCEOT);
#endif
else if (firstch == CAN)
{
if (Lastrx == CAN)
{
log("Sender CANcelled\n");
return(ERROR);
}
else
{
Lastrx = CAN;
continue;
}
}
else if (firstch == TIMEOUT)
{
if (Firstsec)
goto humbug;
bilge:
log("Timeout\n");
}
else
log("Got 0%o sector header\n", firstch);
humbug:
Lastrx = 0;
while (readline(1) != TIMEOUT)
{;}
if (Firstsec)
{
sendline(Crcflg ? WANTCRC : NAK);
Lleft = 0; /* Do read next time ... */
}
else
{
maxtime = 40;
sendline(NAK);
Lleft = 0; /* Do read next time ... */
}
}
/* try to stop the bubble machine. */
canit();
return(ERROR);
}
/*
* This version of readline is reasoably well suited for
* reading many characters.
* (except, currently, for the Regulus version!)
*
* timeout is in tenths of seconds
*/
readline(timeout)
int timeout;
{
int n;
static char *cdq; /* pointer for removing chars from linbuf */
#ifdef MSDOS
time_t start;
#endif
if (--Lleft >= 0)
{
if (Verbose > 8)
fprintf(stderr, "%02x ", *cdq&0377);
return(*cdq++ & Wcsmask);
}
n = timeout/10;
if (n < 2)
n = 3;
if (Verbose > 3)
fprintf(stderr, "Calling read: n=%d ", n);
#ifndef MSDOS
if (setjmp(tohere))
{
#ifdef TIOCFLUSH
/* ioctl(iofd, TIOCFLUSH, 0); */
#endif
Lleft = 0;
if (Verbose > 1)
fprintf(stderr, "Readline:TIMEOUT\n");
return(TIMEOUT);
}
signal(SIGALRM, alrm);
alarm(n);
Lleft = read(iofd, (cdq = linbuf), Readnum);
alarm(0);
#else
time(&start);
while (!(Lleft = tty_read(iofd, (cdq = linbuf), Readnum)) &&
(int)difftime(time(NULL), start) < n)
{;}
if (!Lleft)
{
Lleft = 0;
if (Verbose > 1)
fprintf(stderr, "Readline:TIMEOUT\n");
return(TIMEOUT);
}
#endif
if (Verbose > 3)
fprintf(stderr, "Read returned %d bytes\n", Lleft);
if (Lleft < 1)
return(TIMEOUT);
--Lleft;
if (Verbose > 8)
fprintf(stderr, "%02x ", *cdq&0377);
return(*cdq++ & Wcsmask);
}
/*
* Purge the modem input queue of all characters
*/
purgeline()
{
#ifdef USG
ioctl(iofd, TCFLSH, 0);
#endif
#ifdef V7
lseek(iofd, 0L, 2);
#endif
#ifdef MSDOS
char c;
while (tty_read(iofd, &c, 1))
{;}
#endif
Lleft = 0;
}
/*
* Process incoming file information header
*/
procheader(name)
char *name;
{
char *openmode, *p, **pp;
/* set default parameters and overrides */
#ifndef MSDOS
openmode = "w";
#else
openmode = "wb";
#endif
Thisbinary = Rxbinary || !Rxascii;
if (Lzmanag)
zmanag = Lzmanag;
/*
* Process ZMODEM remote file management requests
*/
if (!Rxbinary && zconv == ZCNL) /* Remote ASCII override */
Thisbinary = 0;
if (zconv == ZCBIN) /* Remote Binary override */
++Thisbinary;
else if (zmanag == ZMAPND)
#ifndef MSDOS
openmode = "a";
#else
openmode = "ab";
#endif
/* ZMPROT check for existing file */
if (zmanag == ZMPROT && (fout = fopen(name, "r")))
{
fclose(fout);
return(ERROR);
}
Bytesleft = DEFBYTL;
Filemode = 0;
Modtime = 0L;
p = name + 1 + strlen(name);
if (*p)
{
/* file coming from Unix or DOS system */
sscanf(p, "%ld%lo%o", &Bytesleft, &Modtime, &Filemode);
if (Filemode & UNIXFILE)
++Thisbinary;
if (Verbose)
fprintf(stderr, "Incoming: %s %ld %lo %o\n",
name, Bytesleft, Modtime, Filemode);
}
else
{
/* File coming from CP/M system */
for (p = name; *p; ++p) /* change / to _ */
if (*p == '/')
*p = '_';
if (*--p == '.') /* zap trailing period */
*p = 0;
}
if (!Zmodem && MakeLCPathname && !IsAnyLower(name))
uncaps(name);
if (Topipe)
{
#ifndef MSDOS
sprintf(Pathname, "%s %s", Progname+2, name);
if (Verbose)
fprintf(stderr, "Topipe: %s %s\n",
Pathname, Thisbinary ? "BIN" : "ASCII");
if ((fout = popen(Pathname, "w")) == NULL)
return(ERROR);
#else
if (Verbose)
fprintf(stderr, "Topipe: stdout %s\n", Thisbinary ? "BIN" : "ASCII");
fout = stdout;
#endif
}
else
{
strcpy(Pathname, name);
if (Verbose)
fprintf(stderr, "Receiving %s %s %s\n",
name, Thisbinary ? "BIN" : "ASCII", openmode);
checkpath(name);
if (Nflag)
#ifndef MSDOS
name = "/dev/null";
#else
name = "nul";
#endif
if ((fout = fopen(name, openmode)) == NULL)
return(ERROR);
}
return(OK);
}
/*
* Putsec writes the n characters of buf to receive file fout.
* If not in binary mode, carriage returns, and all characters
* starting with CPMEOF are discarded.
*/
putsec(buf, n)
char *buf;
int n;
{
char *p;
if (Thisbinary)
for (p = buf; --n >= 0; )
putc(*p++, fout);
else
{
if (Eofseen)
return(OK);
for (p = buf; --n >= 0; ++p )
{
if (*p == '\r')
continue;
if (*p == CPMEOF)
{
Eofseen = TRUE;
return(OK);
}
putc(*p++, fout);
}
}
return(OK);
}
/*
* Send a character to modem. Small is beautiful.
*/
sendline(c)
{
char d;
d = c;
if (Verbose > 4)
fprintf(stderr, "Sendline: %x\n", c);
#ifndef MSDOS
write(1, &d, 1);
#else
tty_write(iofd, &d, 1);
#endif
}
xsendline(c)
{
sendline(c);
}
flushmo()
{;}
/* make string s lower case */
uncaps(s)
char *s;
{
for ( ; *s; ++s)
if (isupper(*s))
*s = tolower(*s);
}
/*
* IsAnyLower returns TRUE if string s has lower case letters.
*/
IsAnyLower(s)
char *s;
{
for ( ; *s; ++s)
if (islower(*s))
return(TRUE);
return(FALSE);
}
/*
* substr(string, token) searches for token in string s
* returns pointer to token within string if found, NULL otherwise
*/
char *substr(s, t)
char *s,*t;
{
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);
}
/*
* Log an error
*/
/*VARARGS1*/
log(s,p,u)
char *s, *p, *u;
{
if (!Verbose)
return;
fprintf(stderr, "error %d: ", errors);
fprintf(stderr, s, p, u);
}
/* 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
};
#ifndef MSDOS
printf(canistr);
#else
tty_write(iofd, canistr, strlen(canistr));
#endif
Lleft = 0; /* Do read next time ... */
fflush(stdout);
}
/*
* Return 1 iff stdout and stderr are different devices
* indicating this program operating with a modem on a
* different line
*/
fromcu()
{
#ifndef MSDOS
struct stat a, b;
fstat(1, &a);
fstat(2, &b);
return(a.st_rdev != b.st_rdev);
#else
return(1);
#endif
}
report(sct)
int sct;
{
if (Verbose > 1)
fprintf(stderr,"%03d%c",sct,sct%10? ' ' : '\r');
}
/*
* If called as [-][dir/../]vrzCOMMAND set Verbose to 1
* If called as [-][dir/../]rzCOMMAND set the pipe flag
* If called as rb use YMODEM protocol
*/
chkinvok(s)
char *s;
{
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] == 'r' && s[1] == 'b')
Nozmodem = TRUE;
if (s[2] && s[0] == 'r' && s[1] == 'b')
Topipe = TRUE;
if (s[2] && s[0] == 'r' && s[1] == 'z')
Topipe = TRUE;
}
/*
* Totalitarian Communist pathname processing
*/
checkpath(name)
char *name;
{
if (Restricted)
{
if (fopen(name, "r") != NULL)
{
canit();
fprintf(stderr, "\r\nrz: %s exists\n", name);
bibi();
}
/* restrict pathnames to current tree or uucppublic */
if (substr(name, "../")
|| (name[0]== '/' && strncmp(name, PUBDIR, strlen(PUBDIR))) )
{
canit();
fprintf(stderr,"\r\nrz:\tSecurity Violation\r\n");
bibi();
}
}
}
/*
* Initialize for Zmodem receive attempt, try to activate Zmodem sender
* Handles ZSINIT frame
* Return ZFILE if Zmodem filename received, -1 on error,
* ZCOMPL if transaction finished, else 0
*/
tryz()
{
int c, n;
int cmdzack1flg;
if (Nozmodem) /* Check for "rb" program name */
return(0);
for (n = Zmodem ? 10 : 5; --n >= 0; )
{
/* Set buffer length (0) and capability flags */
stohdr(0L);
#ifdef CANBREAK
Txhdr[ZF0] = CANFC32|CANFDX|CANOVIO|CANBRK;
#else
Txhdr[ZF0] = CANFC32|CANFDX|CANOVIO;
#endif
zshhdr(tryzhdrtype, Txhdr);
again:
switch (zgethdr(Rxhdr, 0))
{
case ZRQINIT:
continue;
case ZEOF:
continue;
case TIMEOUT:
continue;
case ZFILE:
zconv = Rxhdr[ZF0];
zmanag = Rxhdr[ZF1];
ztrans = Rxhdr[ZF2];
tryzhdrtype = ZRINIT;
if (zrdata(secbuf, KSIZE) == GOTCRCW)
return(ZFILE);
zshhdr(ZNAK, Txhdr);
goto again;
case ZSINIT:
if (zrdata(Attn, ZATTNLEN) == GOTCRCW)
{
zshhdr(ZACK, Txhdr);
goto again;
}
zshhdr(ZNAK, Txhdr);
goto again;
case ZFREECNT:
stohdr(getfree());
zshhdr(ZACK, Txhdr);
goto again;
case ZCOMMAND:
cmdzack1flg = Rxhdr[ZF0];
if (zrdata(secbuf, KSIZE) == GOTCRCW)
{
if (cmdzack1flg & ZCACK1)
stohdr(0L);
else
stohdr((long)sys2(secbuf));
purgeline(); /* dump impatient questions */
do
zshhdr(ZCOMPL, Txhdr);
while (++errors < 10 && zgethdr(Rxhdr, 1) != ZFIN);
ackbibi();
if (cmdzack1flg & ZCACK1)
exec2(secbuf);
return(ZCOMPL);
}
zshhdr(ZNAK, Txhdr);
goto again;
case ZCOMPL:
goto again;
default:
continue;
case ZFIN:
ackbibi();
return(ZCOMPL);
case ZCAN:
return(ERROR);
}
}
return(0);
}
/*
* Receive 1 or more files with ZMODEM protocol
*/
rzfiles()
{
int c;
for (;;)
{
switch (c = rzfile())
{
case ZEOF:
case ZSKIP:
switch (tryz())
{
case ZCOMPL:
return(OK);
default:
return(ERROR);
case ZFILE:
break;
}
continue;
default:
return(c);
case ERROR:
return(ERROR);
}
}
}
/*
* Receive a file with ZMODEM protocol
* Assumes file name frame is in secbuf
*/
rzfile()
{
int c, n;
long rxbytes;
Eofseen = FALSE;
if (procheader(secbuf) == ERROR)
return((tryzhdrtype = ZSKIP));
n = 10;
rxbytes = 0l;
for (;;)
{
stohdr(rxbytes);
zshhdr(ZRPOS, Txhdr);
nxthdr:
switch (c = zgethdr(Rxhdr, 0))
{
default:
vfile("rzfile: zgethdr returned %d", c);
return(ERROR);
case ZNAK:
case TIMEOUT:
if ( --n < 0)
{
vfile("rzfile: zgethdr returned %d", c);
return(ERROR);
}
case ZFILE:
zrdata(secbuf, KSIZE);
continue;
case ZEOF:
if (rclhdr(Rxhdr) != rxbytes)
continue;
if (closeit())
{
tryzhdrtype = ZFERR;
vfile("rzfile: closeit returned <> 0");
return(ERROR);
}
vfile("rzfile: normal EOF");
return(c);
case ERROR: /* Too much garbage in header search error */
if (--n < 0)
{
vfile("rzfile: zgethdr returned %d", c);
return(ERROR);
}
zmputs(Attn);
continue;
case ZDATA:
if (rclhdr(Rxhdr) != rxbytes)
{
if (--n < 0)
return(ERROR);
zmputs(Attn); continue;
}
moredata:
switch (c = zrdata(secbuf, KSIZE))
{
case ZCAN:
vfile("rzfile: zgethdr returned %d", c);
return(ERROR);
case ERROR: /* CRC error */
if (--n < 0)
{
vfile("rzfile: zgethdr returned %d", c);
return(ERROR);
}
zmputs(Attn);
continue;
case TIMEOUT:
if (--n < 0)
{
vfile("rzfile: zgethdr returned %d", c);
return(ERROR);
}
continue;
case GOTCRCW:
n = 10;
putsec(secbuf, Rxcount);
rxbytes += Rxcount;
stohdr(rxbytes);
zshhdr(ZACK, Txhdr);
goto nxthdr;
case GOTCRCQ:
n = 10;
putsec(secbuf, Rxcount);
rxbytes += Rxcount;
stohdr(rxbytes);
zshhdr(ZACK, Txhdr);
goto moredata;
case GOTCRCG:
n = 10;
putsec(secbuf, Rxcount);
rxbytes += Rxcount;
goto moredata;
case GOTCRCE:
n = 10;
putsec(secbuf, Rxcount);
rxbytes += Rxcount;
goto nxthdr;
}
}
}
}
/*
* Send a string to the modem, processing for \336 (sleep 1 sec)
* and \335 (break signal)
*/
zmputs(s)
char *s;
{
int c;
while (*s)
{
switch (c = *s++)
{
case '\336':
sleep(1);
continue;
case '\335':
sendbrk();
continue;
default:
sendline(c);
}
}
}
/*
* Close the receive dataset, return OK or ERROR
*/
closeit()
{
if (Topipe)
{
#ifndef MSDOS
if (pclose(fout))
return(ERROR);
#endif
return(OK);
}
if (fclose(fout) == ERROR)
{
fprintf(stderr, "file close ERROR\n");
return(ERROR);
}
if (Modtime)
{
timep[0] = time(NULL);
#ifndef MSDOS
timep[1] = Modtime;
#else
timep[1] = Modtime - JAN1ST70;
#endif
utime(Pathname, timep);
}
if (Filemode)
chmod(Pathname, (07777 & Filemode));
return(OK);
}
/*
* Ack a ZFIN packet, let byegones be byegones
*/
ackbibi()
{
int n;
vfile("ackbibi:");
Readnum = 1;
stohdr(0L);
for (n = 4; --n>=0; )
{
zshhdr(ZFIN, Txhdr);
for (;;)
{
switch (readline(100))
{
case 'O':
readline(1); /* Discard 2nd 'O' */
/* ***** FALL THRU TO ***** */
case TIMEOUT:
vfile("ackbibi complete");
return;
default:
break;
}
}
}
}
/*
* Local console output simulation
*/
bttyout(c)
{
if (Verbose || fromcu())
putc(c, stderr);
}
/*
* Strip leading ! if present, do shell escape.
*/
sys2(s)
char *s;
{
if (*s == '!')
++s;
return(system(s));
}
/*
* Strip leading ! if present, do exec.
*/
exec2(s)
char *s;
{
if (*s == '!')
++s;
mode(0);
execl("/bin/sh", "sh", "-c", s);
}