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