#define VERSION "1.26 08-21-87"
#define PUBDIR "/usr/spool/uucppublic"

/*% cc -M0 -Ox -K -i % -o rz; size rz;
<-xtx-*> cc386 -Ox rz.c -o $B/rz;  size $B/rz
 *
 * rz.c By Chuck Forsberg
 *
 *	cc -O rz.c -o rz		USG (3.0) Unix
 *	cc -O -DV7  rz.c -o rz		Unix V7, BSD 2.8 - 4.3
 *
 *	ln rz rb;  ln rz rx			For either system
 *
 *	ln rz /usr/bin/rzrmail		For remote mail.  Make this the
 *					login shell. rzrmail then calls
 *					rmail(1) to deliver mail.
 *
 *
 * Amiga version by Frank Harper using Aztec C 3.4b
 *
 *	cc +L rz.c;cc +L SerIO.c;cc +L term.c;ln rz.o SerIO.o term.o -lc32
 *	or just use the makefile
 *
 * See Amiga.doc for more details on Amiga version
 *
 *  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
 *
 *  BIX added 6-30-87 to support BIX(TM) upload protocol used by the
 *  Byte Information Exchange.
 *
 *  NFGVMIN Updated 2-18-87 CAF for Xenix systems where c_cc[VMIN]
 *  doesn't work properly (even though it compiles without error!),
 *
 *  HOWMANY may be tuned for best performance
 *
 *  USG UNIX (3.0) ioctl conventions courtesy  Jeff Martin
 */
#ifdef AMIGA
#define LOGFILE "rzlog"
#include <time.h>
#include <stdio.h>
#include <ctype.h>
#include <stat.h>
#include <devices/timer.h>
#include <devices/serial.h>
#include <libraries/dos.h>
#include "intuition/intuition.h"
#include <exec/memory.h>

#define RZ

void *OpenLibrary();
struct Window *OpenWindow();

struct IntuitionBase *IntuitionBase;
struct NewWindow nw = {
   0, 0,	     /* start position			*/
   640, 120,	     /* width, height			*/
   -1, -1,	     /* detail pen, block pen		*/
   CLOSEWINDOW,      /* IDCMP flags			*/
   ACTIVATE | WINDOWDRAG | WINDOWDEPTH | WINDOWSIZING | WINDOWCLOSE
   | NOCAREREFRESH,  /* window flags			*/
   NULL,	     /* pointer to first user gadget	*/
   NULL,	     /* pointer to user checkmark	*/
   (UBYTE *)"Rz",    /* window title                    */
   NULL,	     /* pointer to screen    (later)    */
   NULL,	     /* pointer to superbitmap		*/
   50,40,-1,-1,      /* sizing limits min and max	*/
   WBENCHSCREEN      /* type of screen in which to open */
   };
int InitSer = FALSE;
int gmt_diff_set=FALSE; /* Has GMT offset been set? */
int gmt_diff;		/* Local offset from GMT time */
int SetProtect=TRUE;
int Term=FALSE; 	/* Use Terminal mode ? */
int WaitC=TRUE; 	/* Wait for confirmation before closing window?*/
struct IOStdReq *ConWriteReq, *ConReadReq;
struct Window *Win;
struct MsgPort	*ConReadPort,*ConWritePort;
int WinOutPut;
int FullDuplex=TRUE;
struct IOExtSer *InSer, *OutSer;
struct MsgPort	*InSerPort, *OutSerPort;
ULONG  InSerSigMask, OutSerSigMask;
UBYTE  SerOpen;
struct timerequest *IOTime;
struct MsgPort *TimerPort;
ULONG  IOTimeSigMask;

int Skip=TRUE;	    /* Skip files which are to big to fit on disk */
#else
#define LOGFILE "/tmp/rzlog"

#include <stdio.h>
#include <signal.h>
#include <setjmp.h>
#include <ctype.h>
FILE *popen();
#endif

#define OK 0

#ifndef AMIGA /* Already defined in <exec/types.h> */
#define FALSE 0
#define TRUE 1
#endif

#define ERROR (-1)

/*
 * Max value for HOWMANY is 255.
 *   A larger value reduces system overhead but may evoke kernel bugs.
 *   133 corresponds to an XMODEM/CRC sector
 *
 * This isn't used in Amiga version
 */
#ifndef HOWMANY
#define HOWMANY 133
#endif

int Zmodem=0;		/* ZMODEM protocol requested */
int Nozmodem = 0;	/* If invoked as "rb" */
unsigned Baudrate;

char *substr();
FILE *fout;

/* Ward Christensen / CP/M parameters - Don't change these! */
#define ENQ 005
#define CAN ('X'&037)
#define XOFF ('s'&037)
#define XON ('q'&037)
#define SOH 1
#define STX 2
#define EOT 4
#define ACK 6
#define NAK 025
#define CPMEOF 032
#define WANTCRC 0103	/* send C not NAK to get crc not checksum */
#define 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;
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 work right in raw mode! */
int Readnum = 1;	/* Number of bytes to ask for in read() from modem */
#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 */
#ifndef AMIGA
short Filemode; 	/* Unix style mode for incoming file */
#else
int Filemode;		/* This avoids a problem with the sscanf used to
			    read Filemode */
#endif
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+1];
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 */
int Zctlesc;		/* Encode control characters */
int Zrwindow = 1400;	/* RX window size (controls garbage count) */

#ifndef AMIGA
jmp_buf tohere; 	/* For the interrupt on RX timeout */
#endif

#include "rbsb.c"       /* most of the system dependent stuff here */
#include "zm.c"

int tryzhdrtype=ZRINIT; /* Header type to send corresponding to Last rx close */

#ifndef AMIGA
/*
 * Routine to calculate the free bytes on the current file system
 *  ~0 means many free bytes (unknown)
 */
long getfree()
{
	return(~0L);    /* many free bytes ... */
}
#else
struct DPTR {			 /* Format of directory fetch pointer */
   struct FileLock *lock;	 /* lock on directory	*/
   struct FileInfoBlock *fib;	 /* mod'd fib for entry */
};

struct DPTR *dopen();
struct FileLock *Lock();
struct FileLock *ParentDir();
struct MsgPort *DeviceProc();
void *AllocMem();
void *malloc();

 /*
  * Calculate free bytes on current file system
  * ~0L means number of free bytes unknown
  * Adapted from shell 2.07's devinfo routine (Thanks Matt & Steve)
  */

long getfree()
{
   struct DPTR		*dp;
   struct InfoData	*info;
   int stat;
   long retc;

   if ((dp = dopen ("", &stat))!=NULL) {
     info = (struct InfoData *)AllocMem((long)sizeof(struct InfoData), MEMF_PUBLIC);
     if (Info (dp->lock, info))
       retc=(info->id_NumBlocks - info->id_NumBlocksUsed)*512; /* assume 512 byte blocks */
     else  {
	if(Verbose) fprintf(stderr,"getfree failed\n");
	retc=~0L;
     }
   }
   FreeMem (info,(long) sizeof(*info));
   dclose(dp);
   if(Verbose) fprintf(stderr,"%ld bytes free on disk\n",retc);
   return(retc);
}

 /*
  * Change a files date
  * Lifted from Shell 2.07
  *
  */

file_date(date,name)
struct DateStamp *date;
char *name;
{
    UBYTE *ptr;
    struct MsgPort *task;
    struct FileLock *dirlock;
    struct DPTR *tmp;
    int stat;
    long ret, dos_packet();

    if (!(task = (struct MsgPort *)DeviceProc(name)))
	return(1);
    if (tmp = dopen(name, &stat)) {
	dirlock = (struct FileLock *)ParentDir(tmp->lock);
	ptr = (UBYTE *)AllocMem(64L,MEMF_PUBLIC);
	strcpy((ptr + 1),tmp->fib->fib_FileName);
	*ptr = strlen(tmp->fib->fib_FileName);
	dclose(tmp);
	ret = dos_packet(task,34L,NULL,dirlock,
			 (ULONG)&ptr[0] >> 2L,date);
	FreeMem(ptr,64L);
	UnLock(dirlock);
    }
}

/*
 * These routines come directly from shell 2.07 by Matt Dillon,
 * Manx version by Steve Drew.
 *
 * Disk directory routines
 *
 * dptr = dopen(name, stat)
 *    struct DPTR *dptr;
 *    char *name;
 *    int *stat;
 *
 * dclose(dptr)                  -may be called with NULL without harm
 *
 * dopen() returns a struct DPTR, or NULL if the given file does not
 * exist.  stat will be set to 1 if the file is a directory.  If the
 * name is "", then the current directory is opened.
 *
 * dclose() closes a directory channel.
 *
 */

struct DPTR *dopen(name, stat)
char *name;
int *stat;
{
   struct DPTR *dp;
   int i;

   *stat = 0;
   dp = (struct DPTR *)malloc(sizeof(struct DPTR));
   dp->lock = (struct FileLock *)Lock (name, ACCESS_READ);
   if (dp->lock == NULL) {
      free (dp);
      if(Verbose) fprintf(stderr,"Couldn't lock dir\n");
      return (NULL);
   }
   dp->fib = (struct FileInfoBlock *)
	 AllocMem((long)sizeof(struct FileInfoBlock), MEMF_PUBLIC);
   if (!Examine (dp->lock, dp->fib)) {
      if(Verbose) fprintf(stderr,"dopen: Can't open current dir\n");
      dclose (dp);
      return (NULL);
   }
   if (dp->fib->fib_DirEntryType >= 0)
      *stat = 1;
   return (dp);
}

dclose(dp)
struct DPTR *dp;
{
   if (dp == NULL)
      return (1);
   if (dp->fib)
      FreeMem (dp->fib,(long)sizeof(*dp->fib));
   if (dp->lock)
      UnLock (dp->lock);
   free (dp);
   return (1);
}
bibi(n)
{
    if (Zmodem)
	zmputs(Attn);
    flushmo();
    canit(); mode(0);
    if(fout) fclose(fout);
    CleanUp();
    exit(128+n);
}

Waitbibi(n)
int n;
{
    ConPutChar(ConWriteReq,7);
    ConPutStr(ConWriteReq,"\nsz: Transfer aborted");
    if( WaitC ) WaitClose();
    bibi(n);
}

WaitClose()
{
 ULONG IntuiMask;

    IntuiMask = 1 << (Win->UserPort->mp_SigBit);
    while(!CheckQuit()) Wait(IntuiMask);
}

AmigaInit()
{
    IntuitionBase = (struct IntuitionBase *)
			OpenLibrary("intuition.library", 33L);
    if (IntuitionBase == NULL) {
	puts("Unable to open 1.2 or higher intuition.library");
	exit(1);
    }
    Win = OpenWindow(&nw);
    if ( Win == NULL ) {
	puts("Unable to open window");
	CleanUp();
	exit(1);
    }
    WinOutPut=TRUE;
    if( OpenConsole(&ConWriteReq,&ConReadReq,Win)) {
	puts("Couldn't open console");
	CleanUp();
	exit(1);
    }
}

CleanUp()
{
    CloseConsole(ConReadReq,ConWriteReq);
    if( Win )         CloseWindow(Win);
    if(IntuitionBase) CloseLibrary(IntuitionBase);
}

 /*
  * Send output to window
  *
  */
fprintf(f,s,a1,a2,a3,a4)
FILE *f;
char *s;
char *a1,*a2,*a3,*a4;
{
 char buf[256];

 sprintf(buf,s,a1,a2,a3,a4);
 if( WinOutPut ) ConPutStr(ConWriteReq,buf);
 else fputs(buf,f);
}
#endif

#ifndef AMIGA
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);
	exit(128+n);
}
#endif

main(argc, argv)
char *argv[];
{
	register char *cp;
	register npats;
	char *virgin, **patts;
	char *getenv();
	int exitcode = 0;

	Rxtimeout = 100;
#ifndef AMIGA
	setbuf(stderr, NULL);
	if ((cp=getenv("SHELL")) && (substr(cp, "rsh") || substr(cp, "rksh")))
		Restricted=TRUE;
#endif

	chkinvok(virgin=argv[0]);       /* if called as [-]rzCOMMAND set flag */
	npats = 0;
#ifdef AMIGA
	AmigaInit();
#endif
	while (--argc) {
		cp = *++argv;
		if (*cp == '-') {
			while( *++cp) {
				switch(*cp) {
				case '+':
					Lzmanag = ZMAPND; break;
#ifndef AMIGA
				case '1':
					iofd = 1; break;
#else
				case 'I':
					InitSer = TRUE; break;
				case 'B':
					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 'e':
					Zctlesc = 1; break;
#ifdef AMIGA
				case 'h':
					FullDuplex = FALSE; break;
				case 'm':
					SetProtect = FALSE; break;
				case 'n':
					Skip = FALSE;  break;
#endif
				case 'p':
					Lzmanag = ZMPROT;  break;
				case 'q':
					Quiet=TRUE; Verbose=0; break;
#ifdef AMIGA
				case 'Q':
					WaitC = FALSE; break;
#endif
				case 't':
					if (--argc < 1) {
						usage();
					}
					Rxtimeout = atoi(*++argv);
					if (Rxtimeout<10 || Rxtimeout>1000)
						usage();
					break;
#ifdef AMIGA
				case 'T':
					Term=TRUE; break;
#endif
				case 'w':
					if (--argc < 1) {
						usage();
					}
					Zrwindow = atoi(*++argv);
					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;
			}
		}
	}
	if (npats > 1)
		usage();
#ifdef AMIGA
	if( mode(1)==ERROR) {
	    fprintf(stderr,"Couldn't initialize serial port\n");
	    Waitbibi(1);
	}
	if (Term) {
	    fprintf(stderr,"Entering terminal mode, click on close box when ready to start file transfer\n");
	    term();
	}
#endif
	if (Verbose) {
		if (freopen(LOGFILE, "a", stderr)==NULL) {
			printf("Can't open log file %s\n",LOGFILE);
			exit(0200);
		}
		setbuf(stderr, NULL);
#ifdef AMIGA
		WinOutPut=FALSE;
#endif
		fprintf(stderr, "argv[0]=%s Progname=%s\n", virgin, Progname);
	}
	if (fromcu() && !Quiet) {
		if (Verbose == 0)
			Verbose = 2;
	}
#ifndef AMIGA
	mode(1);
	if (signal(SIGINT, bibi) == SIG_IGN) {
		signal(SIGINT, SIG_IGN); signal(SIGKILL, SIG_IGN);
	}
	else {
		signal(SIGINT, bibi); signal(SIGKILL, bibi);
	}
	signal(SIGTERM, bibi);
#endif
	if (wcreceive(npats, patts)==ERROR) {
		exitcode=0200;
		canit();
#ifdef AMIGA
		fprintf(stderr,"\n\007File transfer failed\n");
#endif
	}
#ifdef AMIGA
	else fprintf(stderr,"\nFile transfer succesful\n");
	if (Term) {
	    fprintf(stderr,"Re-entering terminal mode. Click on close box to exit Rz.\n");
	    term();
	}
	else if( WaitC ) {
	    fprintf(stderr,"Click on close box to exit Rz\n");
	    WaitClose();
	}
	CleanUp();
#endif
	mode(0);
	if (exitcode && !Zmodem)        /* bellow again with all thy might. */
		canit();
	exit(exitcode);
}

#ifdef AMIGA
char *babble[] = {
    "Usage:  rz [-BIYabehmnv]            (ZMODEM Batch)",
    "or      rb [-Iabhv]             (YMODEM Batch)",
    "or      rz [-Iabchv] file        (XMODEM or XMODEM-1k)",
    "          -I Initialize serial port, using zmodem.init file",
    "          -B Force Ymodem Batch transfer",
    "          -a ASCII transfer (strip CR)",
    "          -b Binary transfer for all files",
    "          -c Use 16 bit CRC     (XMODEM)",
    "          -e Ignore control characters  (ZMODEM)",
    "          -h Half duplex mode (only for Terminal mode)",
    "          -m Don't set file modes",
    "          -n No skipping over files even if to large for disk",
    "          -Q Quit without waiting for click in close box",
    "          -T enter Terminal mode before transferring",
    "          -v Verbose more v's give more info",
    ""
};

usage()
{
  char **pp;
  int i,n,rows,cols,height;
  struct Screen Scr;
  char rep[20];

    /* Expand window, to show as many lines at once as possible */
    MoveWindow(Win,-Win->LeftEdge,-Win->TopEdge);
    if( !GetScreenData(&Scr,sizeof(struct Screen),WBENCHSCREEN)) {
	fprintf(stderr,"Couldn't get screen size");
	CleanUp();
	exit(1);
    }
    rows=Scr.Height;
    cols=Scr.Width;
    SizeWindow(Win,cols>640?640-Win->Width:cols-Win->Width,
		   rows-Win->Height);
    Delay(15);  /* Wait until window is resized */
    ConPutStr(ConWriteReq,"\2330 q"); /* get window bounds */
    n = 0;
    while((rep[n] = ConGetC(ConReadReq)) != 'r' && n++ < 20)
    ;
    height=0;
    i=5;
    while(rep[i]>='0' && rep[i]<='9' && i<n) {
	height*=10;
	height+=rep[i++]-'0';
    }
    for (pp=babble,i=1; **pp; ++pp,i++) {
	fprintf(stderr, "%s\n", *pp);
	if((i%height)==(height-1)) {
	    WaitKey();
	    ConPutStr(ConWriteReq,"\2331\0731\110\233\112");  /* Erase screen */
	}
    }
    fprintf(stderr,"%s by Chuck Forsberg, amiga version 1.0 by Frank Harper\n",VERSION);
    WaitKey();
    bibi(1);
}

WaitKey()
{
  char ConChar;
  ULONG mask,IntuiMask,ConInMask;

    IntuiMask = 1 << (Win->UserPort->mp_SigBit);
    ConInMask = 1 << (ConReadPort->mp_SigBit);
    ConPutStr(ConWriteReq,"\23307\155 Press a key \23300\155");
    QueueRead(ConReadReq,&ConChar);
    mask=Wait(ConInMask|IntuiMask);
    puts("Got something");
    if(CheckQuit()) {       /* Hit close box? */
	KillIO(ConReadReq);
	bibi(1);
    }
    GetMsg(ConReadPort);
}
#else
usage()
{
	fprintf(stderr,"%s %s for %s by Chuck Forsberg\n",
	  Progname, VERSION, OS);
	fprintf(stderr,"Usage:  rz [-1abeuv]            (ZMODEM Batch)\n");
	fprintf(stderr,"or      rb [-1abuv]             (YMODEM Batch)\n");
	fprintf(stderr,"or      rx [-1abcv] file        (XMODEM or XMODEM-1k)\n");
	fprintf(stderr,"          -1 For cu(1): Use fd 1 for input\n");
	fprintf(stderr,"          -a ASCII transfer (strip CR)\n");
	fprintf(stderr,"          -b Binary transfer for all files\n");
	fprintf(stderr,"          -c Use 16 bit CRC     (XMODEM)\n");
	fprintf(stderr,"          -e Ignore control characters  (ZMODEM)\n");
	fprintf(stderr,"          -v Verbose more v's give more info\n");
	exit(1);
}
#endif
/*
 *  Debugging information output interface routine
 */
/* VARARGS1 */
vfile(f, a, b, c)
register char *f;
{
	if (Verbose > 2) {
		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;
{
	register 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;

		procheader(""); strcpy(Pathname, *argp);
#ifndef AMIGA
		checkpath(Pathname);
#endif
		fprintf(stderr, "\nrz: ready to receive %s\r\n", Pathname);
		if ((fout=fopen(Pathname, "w")) == NULL)
			return ERROR;
		if (wcrx()==ERROR)
			goto fubar;
	}
	return OK;
fubar:
	canit();
#ifndef AMIGA
	if (Topipe && fout) {
		pclose(fout);  return ERROR;
	}
#endif
	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) {
		if (c == WCEOT) {
			zperr( "Pathname fetch returned %d", c);
			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, (sectnum&0177)?50:130);
		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)) {
			zperr( "Received dup Sector");
			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 {
			zperr( "Sync Error");
			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;
{
	register checksum, wcj, firstch;
	register unsigned short oldcrc;
	register 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 += (*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)
						zperr( "CRC");
					else {
						Firstsec=FALSE;
						return sectcurr;
					}
				}
				else if (((checksum-firstch)&Wcsmask)==0) {
					Firstsec=FALSE;
					return sectcurr;
				}
				else
					zperr( "Checksum");
			}
			else
				zperr("Sector number garbled");
		}
		/* 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) {
				zperr( "Sender CANcelled");
				return ERROR;
			} else {
				Lastrx=CAN;
				continue;
			}
		}
		else if (firstch==TIMEOUT) {
			if (Firstsec)
				goto humbug;
bilge:
			zperr( "TIMEOUT");
		}
		else
			zperr( "Got 0%o sector header", 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;
}
#ifndef AMIGA
/*
 * 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;
{
	register n;
	static char *cdq;	/* pointer for removing chars from linbuf */

	if (--Lleft >= 0) {
		if (Verbose > 8) {
			fprintf(stderr, "%02x ", *cdq&0377);
		}
		return (*cdq++ & Wcsmask);
	}
	n = timeout/10;
	if (n < 2)
		n = 3;
	if (Verbose > 5)
		fprintf(stderr, "Calling read: alarm=%d  Readnum=%d ",
		  n, Readnum);
	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);
	if (Verbose > 5) {
		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);
}
#else
readline(timeout)
int timeout;
{
   int c;

   c = ReadSer(timeout);
   if(c<0)
    {
	switch(c) {

	  case TIMEOUT:
			Lleft = 0;
			if (Verbose>1)
				fprintf(stderr, "Readline:TIMEOUT\n");
			return TIMEOUT;

	  default:if(Verbose) fprintf(stderr,"readline: unknown error\n");
		  return -1;break;
	}
     }
   else { if(Verbose>8) fprintf(stderr,"%02x ",c&0377);
	   return(c&Wcsmask);
	}
}

#endif /* ifndef AMIGA */


/*
 * Purge the modem input queue of all characters
 */
purgeline()
{
	Lleft = 0;
#ifdef AMIGA
	PurgeSer();
#else
#ifdef USG
	ioctl(iofd, TCFLSH, 0);
#else
	lseek(iofd, 0L, 2);
#endif
#endif /* AMIGA */
}

/*
 * Process incoming file information header
 */
procheader(name)
char *name;
{
	register char *openmode, *p, **pp;

	/* set default parameters and overrides */
	openmode = "w";
	Thisbinary = (!Rxascii) || Rxbinary;
	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 = TRUE;
	else if (zmanag == ZMAPND)
		openmode = "a";

#ifndef BIX
	/* ZMPROT check for existing file */
	if (zmanag == ZMPROT && (fout=fopen(name, "r"))) {
		fclose(fout);  return ERROR;
	}
#endif

	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);
#ifndef AMIGA  /* if the user wants ascii conversion give it to him
		  even if file is coming from UNIX */
		if (Filemode & UNIXFILE)
			++Thisbinary;
#endif
		if (Verbose) {
#ifndef AMIGA
			fprintf(stderr,  "Incoming: %s %ld %lo %o\n",
			  name, Bytesleft, Modtime, Filemode);
#else
			fprintf(stderr,  "\nIncoming: %s length=%ld\n",
			  name, Bytesleft); /* Trying to be a little less
						cryptic than the UNIX version*/
#endif
		}
	}

#ifdef BIX
	if ((fout=fopen("scratchpad", openmode)) == NULL)
		return ERROR;
	return OK;
#else

	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 AMIGA
		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;
#endif
	} else {
		strcpy(Pathname, name);
#ifndef AMIGA
		if (Verbose) {
			fprintf(stderr,  "Receiving %s %s %s\n",
			  name, Thisbinary?"BIN":"ASCII", openmode);
		}
		checkpath(name);
#else
		if (Verbose) {
			fprintf(stderr,  "file transfer mode is %s, file open mode is %s\n",
			  Thisbinary?"BIN":"ASCII", openmode);
		}
#endif
		if (Nflag)
			name = "/dev/null";
		if ((fout=fopen(name, openmode)) == NULL)
			return ERROR;
	}
	return OK;
#endif /* BIX */
}

/*
 * 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;
register n;
{
	register char *p;

	if (Thisbinary) {
		for (p=buf; --n>=0; )
			if( putc( *p++, fout)==EOF )
			    return ERROR;
	}
	else {
		if (Eofseen)
			return OK;
		for (p=buf; --n>=0; ++p ) {
			if ( *p == '\r')
				continue;
			if (*p == CPMEOF) {
				Eofseen=TRUE; return OK;
			}
			if( putc(*p ,fout)==EOF )
			    return ERROR;
		}
	}
	return OK;
}

#ifndef AMIGA
/*
 *  Send a character to modem.	Small is beautiful.
 */
sendline(c)
{
	char d;

	d = c;
	if (Verbose>6)
		fprintf(stderr, "Sendline: %x\n", c);
	write(1, &d, 1);
}
#else
sendline(c)
int c;
{
  char d;
  int retc;

  d=c;
  if(Verbose>6)
	fprintf(stderr,"S:%02x\n",c);
  if( (retc=LWriteSer(&d,1))!=0 )
	  if(Verbose) fprintf(stderr,"sendline:Error sending character %d\n",retc);
}
#endif

xsendline(c)
{
	sendline(c);
}

flushmo() {}




/* make string s lower case */
uncaps(s)
register char *s;
{
	for ( ; *s; ++s)
		if (isupper(*s))
			*s = tolower(*s);
}
/*
 * IsAnyLower returns TRUE if string s has lower case letters.
 */
IsAnyLower(s)
register 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)
register char *s,*t;
{
	register char *ss,*tt;
	/* search for first char of token */
	for (ss=s; *s; s++)
		if (*s == *t)
			/* compare token with substring */
			for (ss=s,tt=t; ;) {
				if (*tt == 0)
					return s;
				if (*ss++ != *tt++)
					break;
			}
	return NULL;
}

/*
 * Log an error
 */
/*VARARGS1*/
zperr(s,p,u)
char *s, *p, *u;
{
	if (Verbose <= 0)
		return;
	fprintf(stderr, "Retry %d: ", errors);
	fprintf(stderr, s, p, u);
	fprintf(stderr, "\n");
}

/* 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 AMIGA
	printf(canistr);
#else
	LWriteSer(canistr,strlen(canistr));
#endif
	Lleft=0;	/* Do read next time ... */
	fflush(stdout);
}

#ifndef AMIGA
/*
 * Return 1 iff stdout and stderr are different devices
 *  indicating this program operating with a modem on a
 *  different line
 */
fromcu()
{
	struct stat a, b;
	fstat(1, &a); fstat(2, &b);
	return (a.st_rdev != b.st_rdev);
}
#else /* AMIGA */
fromcu()
{
 return TRUE;
}
#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;
{
	register char *p;

	p = s;
	while (*p == '-')
		s = ++p;
	while (*p)
		if (*p++ == '/')
			s = p;
	if (*s == 'v') {
		Verbose=1; ++s;
	}
	Progname = s;
	if (s[0]=='r' && s[1]=='b')
		Nozmodem = TRUE;
#ifndef AMIGA
	if (s[2] && s[0]=='r' && s[1]=='b')
		Topipe=TRUE;
	if (s[2] && s[0]=='r' && s[1]=='z')
		Topipe=TRUE;
#endif
}

#ifndef AMIGA
/*
 * 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(-1);
		}
		/* 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(-1);
		}
	}
}
#endif
/*
 * 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()
{
	register c, n;
	register cmdzack1flg;

	if (Nozmodem)           /* Check for "rb" program name */
		return 0;


	for (n=Zmodem?15: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
		if (Zctlesc)
			Txhdr[ZF0] |= TESCCTL;
		zshhdr(tryzhdrtype, Txhdr);
		if (tryzhdrtype == ZSKIP)       /* Don't skip too far */
			tryzhdrtype = ZRINIT;	/* CAF 8-21-87 */
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;
			c = zrdata(secbuf, KSIZE);
			mode(3);
			if (c == GOTCRCW)
				return ZFILE;
			zshhdr(ZNAK, Txhdr);
			goto again;
		case ZSINIT:
			Zctlesc = TESCCTL & Rxhdr[ZF0];
			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<20 && 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()
{
	register 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()
{
	register c, n;
	long rxbytes;

	Eofseen=FALSE;
	if (procheader(secbuf) == ERROR) {
		return (tryzhdrtype = ZSKIP);
	}
#ifdef AMIGA /* skip over file if it won't fit on disk */
	if (Skip && Bytesleft!=DEFBYTL && Bytesleft>getfree()) {
		fprintf(stderr,"rz:Not enough room left on disk for %s. (skipping it)\n",secbuf);
		return (tryzhdrtype = ZSKIP);
	}
#endif
	n = 20; 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) {
				/*
				 * Ignore eof if it's at wrong place - force
				 *  a timeout because the eof might have gone
				 *  out before we sent our zrpos.
				 */
				errors = 0;  goto nxthdr;
			}
			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:
			if (Verbose>1)
				fprintf(stderr, "\r%7ld ZMODEM%s    ",
				  rxbytes, Crc32?" CRC-32":"");
			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 = 20;
				putsec(secbuf, Rxcount);
				rxbytes += Rxcount;
				stohdr(rxbytes);
				zshhdr(ZACK, Txhdr);
				sendline(XON);
				goto nxthdr;
			case GOTCRCQ:
				n = 20;
				putsec(secbuf, Rxcount);
				rxbytes += Rxcount;
				stohdr(rxbytes);
				zshhdr(ZACK, Txhdr);
				goto moredata;
			case GOTCRCG:
				n = 20;
				putsec(secbuf, Rxcount);
				rxbytes += Rxcount;
				goto moredata;
			case GOTCRCE:
				n = 20;
				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;
{
	register 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()
{
#ifdef AMIGA
	long attr;
	int res;
	struct DateStamp dss;
	long time;
#else
	if (Topipe) {
		if (pclose(fout)) {
			return ERROR;
		}
		return OK;
	}
#endif
	if (fclose(fout)==ERROR) {
		fprintf(stderr, "file close ERROR\n");
		return ERROR;
	}
#ifndef AMIGA
	if (Modtime) {
		timep[0] = time(NULL);
		timep[1] = Modtime;
		utime(Pathname, timep);
	}
	if (Filemode)
		chmod(Pathname, (07777 & Filemode));
#else
	if (Modtime && gmt_diff_set) {
	    time=Modtime-(2*366+6*365)*24*60*60 +(gmt_diff * 60 * 60);
	    dss.ds_Tick=(time%60)*TICKS_PER_SECOND;
	    time/=60;
	    dss.ds_Minute=time%(60*24);
	    time/=60;
	    dss.ds_Days=time/24;
	    file_date(&dss,Pathname);
	}
	if (SetProtect && Filemode) {
	    /* Turn off flag to allow the action. */
#ifndef FIBF_SCRIPT
#define FIBF_SCRIPT (1 << 6)
#endif
#ifndef FIBF_PURE
#define FIBF_PURE (1 << 5)
#endif
#ifndef FIBF_HIDDEN
#define FIBF_HIDDEN (1 << 7)
#endif
	    attr = 0L;
	    if(!(Filemode & 0400)) attr |= FIBF_READ;
	    if(!(Filemode & 0200)) attr |= (FIBF_WRITE | FIBF_DELETE);
	    if(!(Filemode & 0100)) attr |= FIBF_EXECUTE;
	    res=SetProtection(Pathname, attr);
	}
#endif
	return OK;
}

/*
 * Ack a ZFIN packet, let byegones be byegones
 */
ackbibi()
{
	register n;

	vfile("ackbibi:");
	Readnum = 1;
	stohdr(0L);
	for (n=3; --n>=0; ) {
		purgeline();
		zshhdr(ZFIN, Txhdr);
		switch (readline(100)) {
		case 'O':
			readline(1);    /* Discard 2nd 'O' */
			vfile("ackbibi complete");
			return;
		case RCDO:
			return;
		case TIMEOUT:
		default:
			break;
		}
	}
}



/*
 * Local console output simulation
 */
bttyout(c)
{
#ifndef AMIGA
	if (Verbose || fromcu())
#else
	if (Verbose>2)
#endif
		putc(c, stderr);
}

#ifndef AMIGA
/*
 * Strip leading ! if present, do shell escape.
 */
sys2(s)
register char *s;
{
	if (*s == '!')
		++s;
	return system(s);
}
/*
 * Strip leading ! if present, do exec.
 */
exec2(s)
register char *s;
{
	if (*s == '!')
		++s;
	mode(0);
	execl("/bin/sh", "sh", "-c", s);
}
#else
sys2(s)
char *s;
{
	if (*s == '!')
		++s;
	/* if it's just an "echo" fake it */
	if(strncmp(s,"echo",4)==0) { printf("%s\n",s+5);return 0; }
	else return(!Execute(s,0L,0L));
}


exec2(s)
char *s;
{
  char cmdbuf[256]; /* Anybody like nice long commands? */

	if (*s == '!')
		++s;
	sprintf(cmdbuf,"run %s",s);
	Execute(cmdbuf,0L,0L);
}
#endif
/* End of rz.c */
