forget difference between big and small commands - obsolete with vm.
[minix.git] / commands / zmodem / rz.c
blob2661eadb53cc818955417c9e5b4758a273f45c50
1 #define VERSION "2.03 05-17-88"
2 #define PUBDIR "/usr/spool/uucppublic"
4 /*% cc -compat -M2 -Ox -K -i -DMD -DOMEN % -o rz; size rz;
5 <-xtx-*> cc386 -Ox -DMD -DOMEN -DSEGMENTS=8 rz.c -o $B/rz; size $B/rz
7 * rz.c By Chuck Forsberg
9 * cc -O rz.c -o rz USG (3.0) Unix
10 * cc -O -DV7 rz.c -o rz Unix V7, BSD 2.8 - 4.3
12 * ln rz rb; ln rz rx For either system
14 * ln rz /usr/bin/rzrmail For remote mail. Make this the
15 * login shell. rzrmail then calls
16 * rmail(1) to deliver mail.
18 * To compile on VMS:
20 * define LNK$LIBRARY SYS$LIBRARY:VAXCRTL.OLB
21 * cc rz.c
22 * cc vvmodem.c
23 * link rz,vvmodem
24 * rz :== $disk:[username.subdir]rz.exe
27 * Unix is a trademark of Western Electric Company
29 * A program for Unix to receive files and commands from computers running
30 * Professional-YAM, PowerCom, YAM, IMP, or programs supporting XMODEM.
31 * rz uses Unix buffered input to reduce wasted CPU time.
33 * Iff the program is invoked by rzCOMMAND, output is piped to
34 * "COMMAND filename" (Unix only)
36 * Some systems (Venix, Coherent, Regulus) may not support tty raw mode
37 * read(2) the same way as Unix. ONEREAD must be defined to force one
38 * character reads for these systems. Added 7-01-84 CAF
40 * Alarm signal handling changed to work with 4.2 BSD 7-15-84 CAF
42 * BIX added 6-30-87 to support BIX(TM) upload protocol used by the
43 * Byte Information Exchange.
45 * NFGVMIN Updated 2-18-87 CAF for Xenix systems where c_cc[VMIN]
46 * doesn't work properly (even though it compiles without error!),
48 * SEGMENTS=n added 2-21-88 as a model for CP/M programs
49 * for CP/M-80 systems that cannot overlap modem and disk I/O.
51 * VMS flavor hacks begin with rz version 2.00
53 * -DMD may be added to compiler command line to compile in
54 * Directory-creating routines from Public Domain TAR by John Gilmore
56 * HOWMANY may be tuned for best performance
58 * USG UNIX (3.0) ioctl conventions courtesy Jeff Martin
61 #include <sys/types.h>
63 #ifdef vax11c
64 #include <types.h>
65 #include <stat.h>
66 #define LOGFILE "rzlog.tmp"
67 #define OS "VMS"
68 #define BUFREAD
69 extern int errno;
70 #define SS_NORMAL SS$_NORMAL
71 #else
72 /* Not vax11c */
73 #define SS_NORMAL 0
74 #define LOGFILE "/tmp/rzlog"
75 #endif
77 #include <time.h>
78 #include <ctype.h>
79 #include <errno.h>
80 #include <signal.h>
81 #include <setjmp.h>
82 #include <string.h>
83 #include <stdlib.h>
84 #include <unistd.h>
85 #include <utime.h>
86 #include <stdio.h>
88 #define OK 0
89 #define FALSE 0
90 #define TRUE 1
91 #undef ERROR
92 #define ERROR (-1)
95 _PROTOTYPE(long getfree , (void));
96 _PROTOTYPE(void alrm , (int sig ));
97 _PROTOTYPE(int main , (int argc , char *argv []));
98 _PROTOTYPE(int usage , (void));
99 _PROTOTYPE(int wcreceive , (int argc , char **argp ));
100 _PROTOTYPE(int wcrxpn , (char *rpn ));
101 _PROTOTYPE(int wcrx , (void));
102 _PROTOTYPE(int wcgetsec , (char *rxbuf , int maxtime ));
103 _PROTOTYPE(int readline , (int timeout ));
104 _PROTOTYPE(void purgeline , (void));
105 _PROTOTYPE(int procheader , (char *name ));
106 _PROTOTYPE(int make_dirs , (char *pathname ));
107 _PROTOTYPE(int makedir , (char *dpath , int dmode ));
108 _PROTOTYPE(int putsec , (char *buf , int n ));
109 _PROTOTYPE(void sendline , (int c ));
110 _PROTOTYPE(void flushmo , (void));
111 _PROTOTYPE(void uncaps , (char *s ));
112 _PROTOTYPE(int IsAnyLower , (char *s ));
113 _PROTOTYPE(char *substr , (char *s , char *t ));
114 void zperr();
115 _PROTOTYPE(void canit , (void));
116 _PROTOTYPE(void report , (int sct ));
117 _PROTOTYPE(void chkinvok , (char *s ));
118 _PROTOTYPE(void checkpath , (char *name ));
119 _PROTOTYPE(int tryz , (void));
120 _PROTOTYPE(int rzfiles , (void));
121 _PROTOTYPE(int rzfile , (void));
122 _PROTOTYPE(void zmputs , (char *s ));
123 _PROTOTYPE(int closeit , (void));
124 _PROTOTYPE(void ackbibi , (void));
125 _PROTOTYPE(void bttyout , (int c ));
126 _PROTOTYPE(int sys2 , (char *s ));
127 _PROTOTYPE(void exec2 , (char *s ));
130 * Max value for HOWMANY is 255.
131 * A larger value reduces system overhead but may evoke kernel bugs.
132 * 133 corresponds to an XMODEM/CRC sector
134 #ifndef HOWMANY
135 #define HOWMANY 133
136 #endif
138 /* Ward Christensen / CP/M parameters - Don't change these! */
139 #define ENQ 005
140 #define CAN ('X'&037)
141 #define XOFF ('s'&037)
142 #define XON ('q'&037)
143 #define SOH 1
144 #define STX 2
145 #define EOT 4
146 #define ACK 6
147 #define NAK 025
148 #define CPMEOF 032
149 #define WANTCRC 0103 /* send C not NAK to get crc not checksum */
150 #define TIMEOUT (-2)
151 #define RCDO (-3)
152 #define ERRORMAX 5
153 #define RETRYMAX 5
154 #define WCEOT (-10)
155 #define PATHLEN 257 /* ready for 4.2 bsd ? */
156 #define UNIXFILE 0xF000 /* The S_IFMT file mask bit for stat */
158 int Zmodem=0; /* ZMODEM protocol requested */
159 int Nozmodem = 0; /* If invoked as "rb" */
160 unsigned Baudrate = 2400;
162 #ifdef vax11c
163 #include "vrzsz.c" /* most of the system dependent stuff here */
164 #else
165 #include "rbsb.c" /* most of the system dependent stuff here */
166 #endif
168 #include "crctab.c"
170 FILE *fout;
173 * Routine to calculate the free bytes on the current file system
174 * ~0 means many free bytes (unknown)
176 long getfree()
178 return(~0L); /* many free bytes ... */
181 int Lastrx;
182 int Crcflg;
183 int Firstsec;
184 int Eofseen; /* indicates cpm eof (^Z) has been received */
185 int errors;
186 int Restricted=0; /* restricted; no /.. or ../ in filenames */
187 #ifdef ONEREAD
188 /* Sorry, Regulus and some others don't work right in raw mode! */
189 int Readnum = 1; /* Number of bytes to ask for in read() from modem */
190 #else
191 int Readnum = HOWMANY; /* Number of bytes to ask for in read() from modem */
192 #endif
194 #define DEFBYTL 2000000000L /* default rx file size */
195 long Bytesleft; /* number of bytes of incoming file left */
196 long Modtime; /* Unix style mod time for incoming file */
197 int Filemode; /* Unix style mode for incoming file */
198 char Pathname[PATHLEN];
199 char *Progname; /* the name by which we were called */
201 int Batch=0;
202 int Topipe=0;
203 int MakeLCPathname=TRUE; /* make received pathname lower case */
204 int Verbose=0;
205 int Quiet=0; /* overrides logic that would otherwise set verbose */
206 int Nflag = 0; /* Don't really transfer files */
207 int Rxclob=FALSE; /* Clobber existing file */
208 int Rxbinary=FALSE; /* receive all files in bin mode */
209 int Rxascii=FALSE; /* receive files in ascii (translate) mode */
210 int Thisbinary; /* current file is to be received in bin mode */
211 int Blklen; /* record length of received packets */
213 #ifdef SEGMENTS
214 int chinseg = 0; /* Number of characters received in this data seg */
215 char secbuf[1+(SEGMENTS+1)*1024];
216 #else
217 char secbuf[1025];
218 #endif
221 char linbuf[HOWMANY];
222 int Lleft=0; /* number of characters in linbuf */
223 time_t timep[2];
224 char Lzmanag; /* Local file management request */
225 char zconv; /* ZMODEM file conversion request */
226 char zmanag; /* ZMODEM file management request */
227 char ztrans; /* ZMODEM file transport request */
228 int Zctlesc; /* Encode control characters */
229 int Zrwindow = 1400; /* RX window size (controls garbage count) */
231 jmp_buf tohere; /* For the interrupt on RX timeout */
233 #define xsendline(c) sendline(c)
234 #include "zm.c"
236 int tryzhdrtype=ZRINIT; /* Header type to send corresponding to Last rx close */
238 void alrm(sig)
239 int sig;
241 longjmp(tohere, -1);
244 /* called by signal interrupt or terminate to clean things up */
245 void bibi(n)
246 int n;
248 if (Zmodem)
249 zmputs(Attn);
250 canit(); mode(0);
251 fprintf(stderr, "rz: caught signal %d; exiting\n", n);
252 cucheck();
253 exit(128+n);
256 int main(argc, argv)
257 int argc;
258 char *argv[];
260 register char *cp;
261 register npats;
262 char *virgin, **patts;
263 int exitcode = 0;
265 Rxtimeout = 100;
266 setbuf(stderr, (char *)NULL);
267 if ((cp=getenv("SHELL")) && (substr(cp, "rsh") || substr(cp, "rksh")))
268 Restricted=TRUE;
270 from_cu();
271 #ifdef vax11c
272 Progname = virgin = "rz";
273 #else
274 chkinvok(virgin=argv[0]); /* if called as [-]rzCOMMAND set flag */
275 #endif
276 npats = 0;
277 while (--argc) {
278 cp = *++argv;
279 if (*cp == '-') {
280 while( *++cp) {
281 switch(*cp) {
282 case '\\':
283 cp[1] = toupper(cp[1]); continue;
284 case '+':
285 Lzmanag = ZMAPND; break;
286 case 'a':
287 Rxascii=TRUE; break;
288 case 'b':
289 Rxbinary=TRUE; break;
290 case 'c':
291 Crcflg=TRUE; break;
292 #ifndef vax11c
293 case 'D':
294 Nflag = TRUE; break;
295 #endif
296 case 'e':
297 Zctlesc = 1; break;
298 case 'p':
299 Lzmanag = ZMPROT; break;
300 case 'q':
301 Quiet=TRUE; Verbose=0; break;
302 case 't':
303 if (--argc < 1) {
304 usage();
306 Rxtimeout = atoi(*++argv);
307 if (Rxtimeout<10 || Rxtimeout>1000)
308 usage();
309 break;
310 case 'w':
311 if (--argc < 1) {
312 usage();
314 Zrwindow = atoi(*++argv);
315 break;
316 case 'u':
317 MakeLCPathname=FALSE; break;
318 case 'v':
319 ++Verbose; break;
320 case 'y':
321 Rxclob=TRUE; break;
322 default:
323 usage();
327 else if ( !npats && argc>0) {
328 if (argv[0][0]) {
329 npats=argc;
330 patts=argv;
334 if (npats > 1)
335 usage();
336 if (Batch && npats)
337 usage();
338 if (Verbose) {
339 if (freopen(LOGFILE, "a", stderr)==NULL) {
340 printf("Can't open log file %s\n",LOGFILE);
341 exit(0200);
343 setbuf(stderr, (char *)NULL);
344 fprintf(stderr, "argv[0]=%s Progname=%s\n", virgin, Progname);
346 if (Fromcu && !Quiet) {
347 if (Verbose == 0)
348 Verbose = 2;
350 vfile("%s %s for %s\n", Progname, VERSION, OS);
351 mode(1);
352 if (signal(SIGINT, bibi) == SIG_IGN) {
353 signal(SIGINT, SIG_IGN); signal(SIGKILL, SIG_IGN);
355 else {
356 signal(SIGINT, bibi); signal(SIGKILL, bibi);
358 signal(SIGTERM, bibi);
359 if (wcreceive(npats, patts)==ERROR) {
360 exitcode=0200;
361 canit();
363 mode(0);
364 vfile("exitcode = %d\n",exitcode);
365 if (exitcode && !Zmodem) /* bellow again with all thy might. */
366 canit();
367 if (exitcode)
368 cucheck();
369 if (Verbose) putc('\n', stderr);
370 exit(exitcode ? exitcode:SS_NORMAL);
374 int usage()
376 cucheck();
377 #ifdef vax11c
378 fprintf(stderr,"Usage: rz [-abeuvy]\n");
379 #else
380 fprintf(stderr,"Usage: rz [-abeuvy] (ZMODEM)\n");
381 fprintf(stderr,"or rb [-abuvy] (YMODEM)\n");
382 fprintf(stderr,"or rx [-abcv] file (XMODEM or XMODEM-1k)\n");
383 #endif
384 fprintf(stderr," -a ASCII transfer (strip CR)\n");
385 fprintf(stderr," -b Binary transfer for all files\n");
386 #ifndef vax11c
387 fprintf(stderr," -c Use 16 bit CRC (XMODEM)\n");
388 #endif
389 fprintf(stderr," -e Escape control characters (ZMODEM)\n");
390 fprintf(stderr," -v Verbose more v's give more info\n");
391 fprintf(stderr," -y Yes, clobber existing file if any\n");
392 fprintf(stderr,"%s %s for %s by Chuck Forsberg, Omen Technology INC\n",
393 Progname, VERSION, OS);
394 fprintf(stderr, "\t\t\042The High Reliability Software\042\n");
395 exit(SS_NORMAL);
398 * Debugging information output interface routine
400 /* VARARGS1 */
401 void vfile(f, a, b, c)
402 register char *f,*a,*b,*c;
405 if (Verbose > 2) {
406 fprintf(stderr, f, a, b, c);
407 fprintf(stderr, "\n");
412 * Let's receive something already.
415 char *rbmsg =
416 "%s ready. To begin transfer, type \"%s file ...\" to your modem program\r\n\n";
418 int wcreceive(argc, argp)
419 int argc;
420 char **argp;
422 register c;
424 if (Batch || argc==0) {
425 Crcflg=1;
426 if ( !Quiet)
427 fprintf(stderr, rbmsg, Progname, Nozmodem?"sb":"sz");
428 if (c=tryz()) {
429 if (c == ZCOMPL)
430 return OK;
431 if (c == ERROR)
432 goto fubar;
433 c = rzfiles();
434 if (c)
435 goto fubar;
436 } else {
437 for (;;) {
438 if (wcrxpn(secbuf)== ERROR)
439 goto fubar;
440 if (secbuf[0]==0)
441 return OK;
442 if (procheader(secbuf) == ERROR)
443 goto fubar;
444 if (wcrx()==ERROR)
445 goto fubar;
448 } else {
449 Bytesleft = DEFBYTL; Filemode = 0; Modtime = 0L;
451 procheader(""); strcpy(Pathname, *argp); checkpath(Pathname);
452 fprintf(stderr, "\nrz: ready to receive %s\r\n", Pathname);
453 if ((fout=fopen(Pathname, "w")) == NULL)
454 return ERROR;
455 if (wcrx()==ERROR)
456 goto fubar;
458 return OK;
459 fubar:
460 canit();
461 #ifndef vax11c
462 if (Topipe && fout) {
463 pclose(fout); return ERROR;
465 #endif
466 if (fout)
467 fclose(fout);
468 #ifndef vax11c
469 if (Restricted) {
470 unlink(Pathname);
471 fprintf(stderr, "\r\nrz: %s removed.\r\n", Pathname);
473 #endif
474 return ERROR;
479 * Fetch a pathname from the other end as a C ctyle ASCIZ string.
480 * Length is indeterminate as long as less than Blklen
481 * A null string represents no more files (YMODEM)
483 int wcrxpn(rpn)
484 char *rpn; /* receive a pathname */
486 register c;
488 #ifdef NFGVMIN
489 readline(1);
490 #else
491 purgeline();
492 #endif
494 et_tu:
495 Firstsec=TRUE; Eofseen=FALSE;
496 sendline(Crcflg?WANTCRC:NAK);
497 Lleft=0; /* Do read next time ... */
498 while ((c = wcgetsec(rpn, 100)) != 0) {
499 if (c == WCEOT) {
500 zperr( "Pathname fetch returned %d", c);
501 sendline(ACK);
502 Lleft=0; /* Do read next time ... */
503 readline(1);
504 goto et_tu;
506 return ERROR;
508 sendline(ACK);
509 return OK;
513 * Adapted from CMODEM13.C, written by
514 * Jack M. Wierda and Roderick W. Hart
517 int wcrx()
519 register int sectnum, sectcurr;
520 register char sendchar;
521 int cblklen; /* bytes to dump this block */
523 Firstsec=TRUE;sectnum=0; Eofseen=FALSE;
524 sendchar=Crcflg?WANTCRC:NAK;
526 for (;;) {
527 sendline(sendchar); /* send it now, we're ready! */
528 Lleft=0; /* Do read next time ... */
529 sectcurr=wcgetsec(secbuf, (sectnum&0177)?50:130);
530 report(sectcurr);
531 if (sectcurr==((sectnum+1) &0377)) {
532 sectnum++;
533 cblklen = Bytesleft>Blklen ? Blklen:Bytesleft;
534 if (putsec(secbuf, cblklen)==ERROR)
535 return ERROR;
536 if ((Bytesleft-=cblklen) < 0)
537 Bytesleft = 0;
538 sendchar=ACK;
540 else if (sectcurr==(sectnum&0377)) {
541 zperr( "Received dup Sector");
542 sendchar=ACK;
544 else if (sectcurr==WCEOT) {
545 if (closeit())
546 return ERROR;
547 sendline(ACK);
548 Lleft=0; /* Do read next time ... */
549 return OK;
551 else if (sectcurr==ERROR)
552 return ERROR;
553 else {
554 zperr( "Sync Error");
555 return ERROR;
561 * Wcgetsec fetches a Ward Christensen type sector.
562 * Returns sector number encountered or ERROR if valid sector not received,
563 * or CAN CAN received
564 * or WCEOT if eot sector
565 * time is timeout for first char, set to 4 seconds thereafter
566 ***************** NO ACK IS SENT IF SECTOR IS RECEIVED OK **************
567 * (Caller must do that when he is good and ready to get next sector)
570 int wcgetsec(rxbuf, maxtime)
571 char *rxbuf;
572 int maxtime;
574 register checksum, wcj, firstch;
575 register unsigned short oldcrc;
576 register char *p;
577 int sectcurr;
579 for (Lastrx=errors=0; errors<RETRYMAX; errors++) {
581 if ((firstch=readline(maxtime))==STX) {
582 Blklen=1024; goto get2;
584 if (firstch==SOH) {
585 Blklen=128;
586 get2:
587 sectcurr=readline(1);
588 if ((sectcurr+(oldcrc=readline(1)))==0377) {
589 oldcrc=checksum=0;
590 for (p=rxbuf,wcj=Blklen; --wcj>=0; ) {
591 if ((firstch=readline(1)) < 0)
592 goto bilge;
593 oldcrc=updcrc(firstch, oldcrc);
594 checksum += (*p++ = firstch);
596 if ((firstch=readline(1)) < 0)
597 goto bilge;
598 if (Crcflg) {
599 oldcrc=updcrc(firstch, oldcrc);
600 if ((firstch=readline(1)) < 0)
601 goto bilge;
602 oldcrc=updcrc(firstch, oldcrc);
603 if (oldcrc & 0xFFFF)
604 zperr( "CRC");
605 else {
606 Firstsec=FALSE;
607 return sectcurr;
610 else if (((checksum-firstch)&0377)==0) {
611 Firstsec=FALSE;
612 return sectcurr;
614 else
615 zperr( "Checksum");
617 else
618 zperr("Sector number garbled");
620 /* make sure eot really is eot and not just mixmash */
621 #ifdef NFGVMIN
622 else if (firstch==EOT && readline(1)==TIMEOUT)
623 return WCEOT;
624 #else
625 else if (firstch==EOT && Lleft==0)
626 return WCEOT;
627 #endif
628 else if (firstch==CAN) {
629 if (Lastrx==CAN) {
630 zperr( "Sender CANcelled");
631 return ERROR;
632 } else {
633 Lastrx=CAN;
634 continue;
637 else if (firstch==TIMEOUT) {
638 if (Firstsec)
639 goto humbug;
640 bilge:
641 zperr( "TIMEOUT");
643 else
644 zperr( "Got 0%o sector header", firstch);
646 humbug:
647 Lastrx=0;
648 while(readline(1)!=TIMEOUT)
650 if (Firstsec) {
651 sendline(Crcflg?WANTCRC:NAK);
652 Lleft=0; /* Do read next time ... */
653 } else {
654 maxtime=40; sendline(NAK);
655 Lleft=0; /* Do read next time ... */
658 /* try to stop the bubble machine. */
659 canit();
660 return ERROR;
663 #ifndef vax11c
665 * This version of readline is reasoably well suited for
666 * reading many characters.
667 * (except, currently, for the Regulus version!)
669 * timeout is in tenths of seconds
671 int readline(timeout)
672 int timeout;
674 register n;
675 static char *cdq; /* pointer for removing chars from linbuf */
677 if (--Lleft >= 0) {
678 if (Verbose > 8) {
679 fprintf(stderr, "%02x ", *cdq&0377);
681 return (*cdq++ & 0377);
683 n = timeout/10;
684 if (n < 2)
685 n = 3;
686 if (Verbose > 5)
687 fprintf(stderr, "Calling read: alarm=%d Readnum=%d ",
688 n, Readnum);
689 if (setjmp(tohere)) {
690 #ifdef TIOCFLUSH
691 /* ioctl(iofd, TIOCFLUSH, 0); */
692 #endif
693 Lleft = 0;
694 if (Verbose>1)
695 fprintf(stderr, "Readline:TIMEOUT\n");
696 return TIMEOUT;
698 signal(SIGALRM, alrm); alarm(n);
699 Lleft=read(iofd, cdq=linbuf, Readnum);
700 alarm(0);
701 if (Verbose > 5) {
702 fprintf(stderr, "Read returned %d bytes\n", Lleft);
704 if (Lleft < 1)
705 return TIMEOUT;
706 --Lleft;
707 if (Verbose > 8) {
708 fprintf(stderr, "%02x ", *cdq&0377);
710 return (*cdq++ & 0377);
716 * Purge the modem input queue of all characters
718 void purgeline()
720 Lleft = 0;
721 #ifdef USG
722 ioctl(iofd, TCFLSH, 0);
723 #else
724 lseek(iofd, 0L, 2);
725 #endif
727 #endif
731 * Process incoming file information header
733 int procheader(name)
734 char *name;
736 register char *openmode, *p;
738 /* set default parameters and overrides */
739 openmode = "w";
740 Thisbinary = (!Rxascii) || Rxbinary;
741 if (Lzmanag)
742 zmanag = Lzmanag;
745 * Process ZMODEM remote file management requests
747 if (!Rxbinary && zconv == ZCNL) /* Remote ASCII override */
748 Thisbinary = 0;
749 if (zconv == ZCBIN) /* Remote Binary override */
750 Thisbinary = TRUE;
751 else if (zmanag == ZMAPND)
752 openmode = "a";
754 #ifndef BIX
755 /* Check for existing file */
756 if (!Rxclob && (zmanag&ZMMASK) != ZMCLOB && (fout=fopen(name, "r"))) {
757 fclose(fout); return ERROR;
759 #endif
761 Bytesleft = DEFBYTL; Filemode = 0; Modtime = 0L;
763 p = name + 1 + strlen(name);
764 if (*p) { /* file coming from Unix or DOS system */
765 sscanf(p, "%ld%lo%o", &Bytesleft, &Modtime, &Filemode);
766 #ifndef vax11c
767 if (Filemode & UNIXFILE)
768 ++Thisbinary;
769 #endif
770 if (Verbose) {
771 fprintf(stderr, "\nIncoming: %s %ld %lo %o\n",
772 name, Bytesleft, Modtime, Filemode);
776 #ifdef BIX
777 if ((fout=fopen("scratchpad", openmode)) == NULL)
778 return ERROR;
779 return OK;
780 #else
782 else { /* File coming from CP/M system */
783 for (p=name; *p; ++p) /* change / to _ */
784 if ( *p == '/')
785 *p = '_';
787 if ( *--p == '.') /* zap trailing period */
788 *p = 0;
791 #ifndef vax11c
792 if (!Zmodem && MakeLCPathname && !IsAnyLower(name)
793 && !(Filemode&UNIXFILE))
794 uncaps(name);
795 #endif
796 if (Topipe > 0) {
797 sprintf(Pathname, "%s %s", Progname+2, name);
798 if (Verbose)
799 fprintf(stderr, "Topipe: %s %s\n",
800 Pathname, Thisbinary?"BIN":"ASCII");
801 #ifndef vax11c
802 if ((fout=popen(Pathname, "w")) == NULL)
803 return ERROR;
804 #endif
805 } else {
806 strcpy(Pathname, name);
807 if (Verbose) {
808 fprintf(stderr, "Receiving %s %s %s\n",
809 name, Thisbinary?"BIN":"ASCII", openmode);
811 checkpath(name);
812 if (Nflag)
813 name = "/dev/null";
814 #ifndef vax11c
815 #ifdef OMEN
816 if (name[0] == '!' || name[0] == '|') {
817 if ( !(fout = popen(name+1, "w"))) {
818 return ERROR;
820 Topipe = -1; return(OK);
822 #endif
823 #endif
824 #ifdef MD
825 fout = fopen(name, openmode);
826 if ( !fout)
827 if (make_dirs(name))
828 fout = fopen(name, openmode);
829 #else
830 fout = fopen(name, openmode);
831 #endif
832 if ( !fout)
833 return ERROR;
835 return OK;
836 #endif /* BIX */
839 #ifdef MD
841 * Directory-creating routines from Public Domain TAR by John Gilmore
845 * After a file/link/symlink/dir creation has failed, see if
846 * it's because some required directory was not present, and if
847 * so, create all required dirs.
849 int make_dirs(pathname)
850 register char *pathname;
852 register char *p; /* Points into path */
853 int madeone = 0; /* Did we do anything yet? */
854 int save_errno = errno; /* Remember caller's errno */
855 char *strchr();
857 if (errno != ENOENT)
858 return 0; /* Not our problem */
860 for (p = strchr(pathname, '/'); p != NULL; p = strchr(p+1, '/')) {
861 /* Avoid mkdir of empty string, if leading or double '/' */
862 if (p == pathname || p[-1] == '/')
863 continue;
864 /* Avoid mkdir where last part of path is '.' */
865 if (p[-1] == '.' && (p == pathname+1 || p[-2] == '/'))
866 continue;
867 *p = 0; /* Truncate the path there */
868 if ( !makedir(pathname, 0777)) { /* Try to create it as a dir */
869 vfile("Made directory %s\n", pathname);
870 madeone++; /* Remember if we made one */
871 *p = '/';
872 continue;
874 *p = '/';
875 if (errno == EEXIST) /* Directory already exists */
876 continue;
878 * Some other error in the makedir. We return to the caller.
880 break;
882 errno = save_errno; /* Restore caller's errno */
883 return madeone; /* Tell them to retry if we made one */
886 #if (MD != 2)
887 #define TERM_SIGNAL(status) ((status) & 0x7F)
888 #define TERM_COREDUMP(status) (((status) & 0x80) != 0)
889 #define TERM_VALUE(status) ((status) >> 8)
891 * Make a directory. Compatible with the mkdir() system call on 4.2BSD.
893 int makedir(dpath, dmode)
894 char *dpath;
895 int dmode;
897 int cpid, status;
898 struct stat statbuf;
900 if (stat(dpath,&statbuf) == 0) {
901 errno = EEXIST; /* Stat worked, so it already exists */
902 return -1;
905 /* If stat fails for a reason other than non-existence, return error */
906 if (errno != ENOENT) return -1;
908 switch (cpid = fork()) {
910 case -1: /* Error in fork() */
911 return(-1); /* Errno is set already */
913 case 0: /* Child process */
915 * Cheap hack to set mode of new directory. Since this
916 * child process is going away anyway, we zap its umask.
917 * FIXME, this won't suffice to set SUID, SGID, etc. on this
918 * directory. Does anybody care?
920 status = umask(0); /* Get current umask */
921 status = umask(status | (0777 & ~dmode)); /* Set for mkdir */
922 execl("/bin/mkdir", "mkdir", dpath, (char *)0);
923 _exit(-1); /* Can't exec /bin/mkdir */
925 default: /* Parent process */
926 while (cpid != wait(&status)) ; /* Wait for kid to finish */
929 if (TERM_SIGNAL(status) != 0 || TERM_VALUE(status) != 0) {
930 errno = EIO; /* We don't know why, but */
931 return -1; /* /bin/mkdir failed */
934 return 0;
936 #endif /* MD != 2 */
937 #endif /* MD */
940 * Putsec writes the n characters of buf to receive file fout.
941 * If not in binary mode, carriage returns, and all characters
942 * starting with CPMEOF are discarded.
944 int putsec(buf, n)
945 char *buf;
946 register int n;
948 register char *p;
950 if (n == 0)
951 return OK;
952 if (Thisbinary) {
953 for (p=buf; --n>=0; )
954 putc( *p++, fout);
956 else {
957 if (Eofseen)
958 return OK;
959 for (p=buf; --n>=0; ++p ) {
960 if ( *p == '\r')
961 continue;
962 if (*p == CPMEOF) {
963 Eofseen=TRUE; return OK;
965 putc(*p ,fout);
968 return OK;
971 #ifndef vax11c
973 * Send a character to modem. Small is beautiful.
975 void sendline(c)
976 int c;
978 char d;
980 d = c;
981 if (Verbose>6)
982 fprintf(stderr, "Sendline: %x\n", c);
983 write(1, &d, 1);
986 void flushmo() {}
987 #endif
993 /* make string s lower case */
994 void uncaps(s)
995 register char *s;
997 for ( ; *s; ++s)
998 if (isupper(*s))
999 *s = tolower(*s);
1002 * IsAnyLower returns TRUE if string s has lower case letters.
1004 int IsAnyLower(s)
1005 register char *s;
1007 for ( ; *s; ++s)
1008 if (islower(*s))
1009 return TRUE;
1010 return FALSE;
1014 * substr(string, token) searches for token in string s
1015 * returns pointer to token within string if found, NULL otherwise
1017 char *
1018 substr(s, t)
1019 register char *s,*t;
1021 register char *ss,*tt;
1022 /* search for first char of token */
1023 for (ss=s; *s; s++)
1024 if (*s == *t)
1025 /* compare token with substring */
1026 for (ss=s,tt=t; ;) {
1027 if (*tt == 0)
1028 return s;
1029 if (*ss++ != *tt++)
1030 break;
1032 return (char *)NULL;
1036 * Log an error
1038 /*VARARGS1*/
1039 void zperr(s,p,u)
1040 char *s, *p, *u;
1042 if (Verbose <= 0)
1043 return;
1044 fprintf(stderr, "Retry %d: ", errors);
1045 fprintf(stderr, s, p, u);
1046 fprintf(stderr, "\n");
1049 /* send cancel string to get the other end to shut up */
1050 void canit()
1052 static char canistr[] = {
1053 24,24,24,24,24,24,24,24,24,24,8,8,8,8,8,8,8,8,8,8,0
1056 #ifdef vax11c
1057 raw_wbuf(strlen(canistr), canistr);
1058 purgeline();
1059 #else
1060 printf(canistr);
1061 Lleft=0; /* Do read next time ... */
1062 fflush(stdout);
1063 #endif
1067 void report(sct)
1068 int sct;
1070 if (Verbose>1)
1071 fprintf(stderr,"%03d%c",sct,sct%10? ' ' : '\r');
1074 #ifndef vax11c
1076 * If called as [-][dir/../]vrzCOMMAND set Verbose to 1
1077 * If called as [-][dir/../]rzCOMMAND set the pipe flag
1078 * If called as rb use YMODEM protocol
1080 void chkinvok(s)
1081 char *s;
1083 register char *p;
1085 p = s;
1086 while (*p == '-')
1087 s = ++p;
1088 while (*p)
1089 if (*p++ == '/')
1090 s = p;
1091 if (*s == 'v') {
1092 Verbose=1; ++s;
1094 Progname = s;
1095 if (s[0]=='r' && s[1]=='z')
1096 Batch = TRUE;
1097 if (s[0]=='r' && s[1]=='b')
1098 Batch = Nozmodem = TRUE;
1099 if (s[2] && s[0]=='r' && s[1]=='b')
1100 Topipe = 1;
1101 if (s[2] && s[0]=='r' && s[1]=='z')
1102 Topipe = 1;
1104 #endif
1107 * Totalitarian Communist pathname processing
1109 void checkpath(name)
1110 char *name;
1112 if (Restricted) {
1113 if (fopen(name, "r") != NULL) {
1114 canit();
1115 fprintf(stderr, "\r\nrz: %s exists\n", name);
1116 bibi(-1);
1118 /* restrict pathnames to current tree or uucppublic */
1119 if ( substr(name, "../")
1120 || (name[0]== '/' && strncmp(name, PUBDIR, strlen(PUBDIR))) ) {
1121 canit();
1122 fprintf(stderr,"\r\nrz:\tSecurity Violation\r\n");
1123 bibi(-1);
1129 * Initialize for Zmodem receive attempt, try to activate Zmodem sender
1130 * Handles ZSINIT frame
1131 * Return ZFILE if Zmodem filename received, -1 on error,
1132 * ZCOMPL if transaction finished, else 0
1134 int tryz()
1136 register c, n;
1137 register cmdzack1flg;
1139 if (Nozmodem) /* Check for "rb" program name */
1140 return 0;
1143 for (n=Zmodem?15:5; --n>=0; ) {
1144 /* Set buffer length (0) and capability flags */
1145 #ifdef SEGMENTS
1146 stohdr(SEGMENTS*1024L);
1147 #else
1148 stohdr(0L);
1149 #endif
1150 #ifdef CANBREAK
1151 Txhdr[ZF0] = CANFC32|CANFDX|CANOVIO|CANBRK;
1152 #else
1153 Txhdr[ZF0] = CANFC32|CANFDX|CANOVIO;
1154 #endif
1155 if (Zctlesc)
1156 Txhdr[ZF0] |= TESCCTL;
1157 zshhdr(tryzhdrtype, Txhdr);
1158 if (tryzhdrtype == ZSKIP) /* Don't skip too far */
1159 tryzhdrtype = ZRINIT; /* CAF 8-21-87 */
1160 again:
1161 switch (zgethdr(Rxhdr, 0)) {
1162 case ZRQINIT:
1163 continue;
1164 case ZEOF:
1165 continue;
1166 case TIMEOUT:
1167 continue;
1168 case ZFILE:
1169 zconv = Rxhdr[ZF0];
1170 zmanag = Rxhdr[ZF1];
1171 ztrans = Rxhdr[ZF2];
1172 tryzhdrtype = ZRINIT;
1173 c = zrdata(secbuf, 1024);
1174 mode(3);
1175 if (c == GOTCRCW)
1176 return ZFILE;
1177 zshhdr(ZNAK, Txhdr);
1178 goto again;
1179 case ZSINIT:
1180 Zctlesc = TESCCTL & Rxhdr[ZF0];
1181 if (zrdata(Attn, ZATTNLEN) == GOTCRCW) {
1182 stohdr(1L);
1183 zshhdr(ZACK, Txhdr);
1184 goto again;
1186 zshhdr(ZNAK, Txhdr);
1187 goto again;
1188 case ZFREECNT:
1189 stohdr(getfree());
1190 zshhdr(ZACK, Txhdr);
1191 goto again;
1192 case ZCOMMAND:
1193 #ifdef vax11c
1194 return ERROR;
1195 #else
1196 cmdzack1flg = Rxhdr[ZF0];
1197 if (zrdata(secbuf, 1024) == GOTCRCW) {
1198 if (cmdzack1flg & ZCACK1)
1199 stohdr(0L);
1200 else
1201 stohdr((long)sys2(secbuf));
1202 purgeline(); /* dump impatient questions */
1203 do {
1204 zshhdr(ZCOMPL, Txhdr);
1206 while (++errors<20 && zgethdr(Rxhdr,1) != ZFIN);
1207 ackbibi();
1208 if (cmdzack1flg & ZCACK1)
1209 exec2(secbuf);
1210 return ZCOMPL;
1212 zshhdr(ZNAK, Txhdr); goto again;
1213 #endif
1214 case ZCOMPL:
1215 goto again;
1216 default:
1217 continue;
1218 case ZFIN:
1219 ackbibi(); return ZCOMPL;
1220 case ZCAN:
1221 return ERROR;
1224 return 0;
1228 * Receive 1 or more files with ZMODEM protocol
1230 int rzfiles()
1232 register c;
1234 for (;;) {
1235 switch (c = rzfile()) {
1236 case ZEOF:
1237 case ZSKIP:
1238 switch (tryz()) {
1239 case ZCOMPL:
1240 return OK;
1241 default:
1242 return ERROR;
1243 case ZFILE:
1244 break;
1246 continue;
1247 default:
1248 return c;
1249 case ERROR:
1250 return ERROR;
1256 * Receive a file with ZMODEM protocol
1257 * Assumes file name frame is in secbuf
1259 int rzfile()
1261 register c, n;
1262 long rxbytes;
1264 Eofseen=FALSE;
1265 if (procheader(secbuf) == ERROR) {
1266 return (tryzhdrtype = ZSKIP);
1269 n = 20; rxbytes = 0l;
1271 for (;;) {
1272 #ifdef SEGMENTS
1273 chinseg = 0;
1274 #endif
1275 stohdr(rxbytes);
1276 zshhdr(ZRPOS, Txhdr);
1277 nxthdr:
1278 switch (c = zgethdr(Rxhdr, 0)) {
1279 default:
1280 vfile("rzfile: zgethdr returned %d", c);
1281 return ERROR;
1282 case ZNAK:
1283 case TIMEOUT:
1284 #ifdef SEGMENTS
1285 putsec(secbuf, chinseg);
1286 chinseg = 0;
1287 #endif
1288 if ( --n < 0) {
1289 vfile("rzfile: zgethdr returned %d", c);
1290 return ERROR;
1292 case ZFILE:
1293 zrdata(secbuf, 1024);
1294 continue;
1295 case ZEOF:
1296 #ifdef SEGMENTS
1297 putsec(secbuf, chinseg);
1298 chinseg = 0;
1299 #endif
1300 if (rclhdr(Rxhdr) != rxbytes) {
1302 * Ignore eof if it's at wrong place - force
1303 * a timeout because the eof might have gone
1304 * out before we sent our zrpos.
1306 errors = 0; goto nxthdr;
1308 if (closeit()) {
1309 tryzhdrtype = ZFERR;
1310 vfile("rzfile: closeit returned <> 0");
1311 return ERROR;
1313 vfile("rzfile: normal EOF");
1314 return c;
1315 case ERROR: /* Too much garbage in header search error */
1316 #ifdef SEGMENTS
1317 putsec(secbuf, chinseg);
1318 chinseg = 0;
1319 #endif
1320 if ( --n < 0) {
1321 vfile("rzfile: zgethdr returned %d", c);
1322 return ERROR;
1324 zmputs(Attn);
1325 continue;
1326 case ZSKIP:
1327 #ifdef SEGMENTS
1328 putsec(secbuf, chinseg);
1329 chinseg = 0;
1330 #endif
1331 closeit();
1332 vfile("rzfile: Sender SKIPPED file");
1333 return c;
1334 case ZDATA:
1335 if (rclhdr(Rxhdr) != rxbytes) {
1336 if ( --n < 0) {
1337 return ERROR;
1339 #ifdef SEGMENTS
1340 putsec(secbuf, chinseg);
1341 chinseg = 0;
1342 #endif
1343 zmputs(Attn); continue;
1345 moredata:
1346 if (Verbose>1)
1347 fprintf(stderr, "\r%7ld ZMODEM%s ",
1348 rxbytes, Crc32?" CRC-32":"");
1349 #ifdef SEGMENTS
1350 if (chinseg >= (1024 * SEGMENTS)) {
1351 putsec(secbuf, chinseg);
1352 chinseg = 0;
1354 switch (c = zrdata(secbuf+chinseg, 1024))
1355 #else
1356 switch (c = zrdata(secbuf, 1024))
1357 #endif
1359 case ZCAN:
1360 #ifdef SEGMENTS
1361 putsec(secbuf, chinseg);
1362 chinseg = 0;
1363 #endif
1364 vfile("rzfile: zgethdr returned %d", c);
1365 return ERROR;
1366 case ERROR: /* CRC error */
1367 #ifdef SEGMENTS
1368 putsec(secbuf, chinseg);
1369 chinseg = 0;
1370 #endif
1371 if ( --n < 0) {
1372 vfile("rzfile: zgethdr returned %d", c);
1373 return ERROR;
1375 zmputs(Attn);
1376 continue;
1377 case TIMEOUT:
1378 #ifdef SEGMENTS
1379 putsec(secbuf, chinseg);
1380 chinseg = 0;
1381 #endif
1382 if ( --n < 0) {
1383 vfile("rzfile: zgethdr returned %d", c);
1384 return ERROR;
1386 continue;
1387 case GOTCRCW:
1388 n = 20;
1389 #ifdef SEGMENTS
1390 chinseg += Rxcount;
1391 putsec(secbuf, chinseg);
1392 chinseg = 0;
1393 #else
1394 putsec(secbuf, Rxcount);
1395 #endif
1396 rxbytes += Rxcount;
1397 stohdr(rxbytes);
1398 zshhdr(ZACK, Txhdr);
1399 sendline(XON);
1400 goto nxthdr;
1401 case GOTCRCQ:
1402 n = 20;
1403 #ifdef SEGMENTS
1404 chinseg += Rxcount;
1405 #else
1406 putsec(secbuf, Rxcount);
1407 #endif
1408 rxbytes += Rxcount;
1409 stohdr(rxbytes);
1410 zshhdr(ZACK, Txhdr);
1411 goto moredata;
1412 case GOTCRCG:
1413 n = 20;
1414 #ifdef SEGMENTS
1415 chinseg += Rxcount;
1416 #else
1417 putsec(secbuf, Rxcount);
1418 #endif
1419 rxbytes += Rxcount;
1420 goto moredata;
1421 case GOTCRCE:
1422 n = 20;
1423 #ifdef SEGMENTS
1424 chinseg += Rxcount;
1425 #else
1426 putsec(secbuf, Rxcount);
1427 #endif
1428 rxbytes += Rxcount;
1429 goto nxthdr;
1436 * Send a string to the modem, processing for \336 (sleep 1 sec)
1437 * and \335 (break signal)
1439 void zmputs(s)
1440 char *s;
1442 register c;
1444 while (*s) {
1445 switch (c = *s++) {
1446 case '\336':
1447 sleep(1); continue;
1448 case '\335':
1449 sendbrk(); continue;
1450 default:
1451 sendline(c);
1457 * Close the receive dataset, return OK or ERROR
1459 int closeit()
1461 time_t q;
1463 #ifndef vax11c
1464 if (Topipe) {
1465 if (pclose(fout)) {
1466 return ERROR;
1468 return OK;
1470 #endif
1471 if (fclose(fout)==ERROR) {
1472 fprintf(stderr, "file close ERROR\n");
1473 return ERROR;
1475 #ifndef vax11c
1476 if (Modtime) {
1477 timep[0] = time(&q);
1478 timep[1] = Modtime;
1479 utime(Pathname, (struct utimbuf *) timep);
1481 #endif
1482 if ((Filemode&S_IFMT) == S_IFREG)
1483 chmod(Pathname, (07777 & Filemode));
1484 return OK;
1488 * Ack a ZFIN packet, let byegones be byegones
1490 void ackbibi()
1492 register n;
1494 vfile("ackbibi:");
1495 Readnum = 1;
1496 stohdr(0L);
1497 for (n=3; --n>=0; ) {
1498 purgeline();
1499 zshhdr(ZFIN, Txhdr);
1500 switch (readline(100)) {
1501 case 'O':
1502 readline(1); /* Discard 2nd 'O' */
1503 vfile("ackbibi complete");
1504 return;
1505 case RCDO:
1506 return;
1507 case TIMEOUT:
1508 default:
1509 break;
1517 * Local console output simulation
1519 void bttyout(c)
1520 int c;
1522 if (Verbose || Fromcu)
1523 putc(c, stderr);
1526 #ifndef vax11c
1528 * Strip leading ! if present, do shell escape.
1530 int sys2(s)
1531 register char *s;
1533 if (*s == '!')
1534 ++s;
1535 return system(s);
1538 * Strip leading ! if present, do exec.
1540 void exec2(s)
1541 register char *s;
1543 if (*s == '!')
1544 ++s;
1545 mode(0);
1546 execl("/bin/sh", "sh", "-c", s);
1548 #endif
1549 /* End of rz.c */