/****************************************************************************
*	Language 	:	Turbo C 2.0												*
*	Logfile		:	rz.c													*
*	Project		:	Comms library.											*
*	Date 		:	24 Jan 90												*
*	Revision 	:	1.1		GT	PC version.									*
*	07 Mar 90	:	1.2		GT	Background transfer.						*
*	25 Oct 92	:	1.3		GT	KA9Q mods.									*
*	30 Jan 93	:	1.4		GT	Fix KA9Q background transfer.				*
*	20 Feb 93	:	1.5		GT	Bump HOWMANY to 2048.						*
*	21 Feb 93	:	1.6		GT	Allocate buffer on heap.					*
*	08 May 93	:	1.7		GT	Fix warnings.								*
*****************************************************************************
*	Purpose		:	File receive driver.									*
*****************************************************************************
*				:	This module is based on the equivalent module in the	*
*				:	31 Aug 87 version of rz/sz.								*
*	$Id: rz.c 1.5 93/07/16 11:49:53 ROOT_DOS Exp $
*
*  ATARI Version by David Nash - dnash@chaos.demon.co.uk
*
****************************************************************************/

#include	<io.h>
#include	<fcntl.h>
#include	<stdio.h>
#include	<stdlib.h>
#include	<signal.h>
#include	<setjmp.h>
#include	<ctype.h>
#include	<time.h>
#include	<string.h>
#include	<process.h>
#include	<sys/types.h>
#include	<sys/stat.h>
#include	<dos.h>
#ifdef ATARI
#include <ext.h>
#endif
#include	"zmodem.h"
#include	"sz.h"
#include	"rbsb.h"		/* most of the system dependent stuff here		*/
#include	"zm.h"
#include	"global.h"
#ifdef	DEBUGZ
#include	"tty.h"
#endif


/*
 * Max value for HOWMANY is 255.
 *   A larger value reduces system overhead but may evoke kernel bugs.
 *   133 corresponds to an XMODEM/CRC sector
 */

#ifndef HOWMANY
#define HOWMANY 2048			/* was 133										*/
#endif

#undef	RETRYMAX
#define RETRYMAX	5

#define UNIXFILE 0x8000		/* the S_IFREG file mask bit for stat			*/
#define DEFBYTL 2000000000L	/* default rx file size							*/
#define	CANBREAK

static int fout;
static int Eofseen;			/* indicates cpm eof (^Z) has been received		*/

int Readnum = HOWMANY; 		/* Number of bytes to ask for in read () from
							     modem 										*/

static long Bytesleft;		/* number of bytes of incoming file left		*/
static long Modtime;		/* Unix style mod time for incoming file		*/
static short Filemode;		/* Unix style mode for incoming file			*/
static char Pathname[PATHLEN];

static int Batch = 0;
static int MakeLCPathname = TRUE; /* make received pathname lower case		*/
static int Nflag = 0;		/* Don't really transfer files					*/
static int Rxbinary = FALSE; /* receive all files in bin mode				*/
static int Rxascii = FALSE;	/* receive files in ascii (translate) mode		*/
static int Thisbinary;		/* current file is to be received in bin mode	*/
static int rz_blklen;		/* record length of received packets			*/
static char secbuf[KSIZE + 1];
#if	0
static char linbuf[HOWMANY];
#endif
static char *linbuf = 0;	/* -> receive buffer							*/
int Lleft = 0;				/* number of characters in linbuf				*/
static int Filcnt = 0;		/* no of files transferred						*/

#if	0
time_t timep[2];
#endif
static int nocommand = FALSE; /* TRUE - disallow remote commands			*/

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


/****************************************************************************
*	Local prototypes.														*
****************************************************************************/

#if	0
static void bibi (int n);
#endif
static void chkinvok (char protocol);
static void purgeline (void);
static void uncaps (char *s);
static int IsAnyLower (char *s);
static void canit (void);
static void report (int sct);
static void checkpath (char *name);
static int tryz (void);
static int rzfiles (void);
static int rzfile (void);
static void zmputs (char *s);
static int closeit (void);
static void ackbibi (void);
static int sys2 (char *s);
static void exec2 (char *s);
static long getfree (void);
static int wcreceive (int argc, char **argp);
static int wcrxpn (char *rpn);
static int wcrx (void);
static int wcgetsec (char *rxbuf, int maxtime);
static int procheader (char *name);
static int putsec (char *buf, int n);

							   
/****************************************************************************
*	getfree																	*
*	Routine to calculate the free bytes on the current file system.			*
*	~0 means many free bytes (unknown).										*
****************************************************************************/

static long getfree (void)
	{
	return (~0L);	/* many free bytes ... */
	}	/* static long getfree (void) */


#if	0
/****************************************************************************
*	bibi																	*
*	Called by signal interrupt or terminate to clean things up.				*
****************************************************************************/
static void bibi (int n)
	{
	n = n;
	if (_Zmodem)
		{
#ifdef	DEBUGZ
		_tout ("\r\n\nbibi: calling zmputs (_Attn)\r\n");
#endif
		zmputs (_Attn);
		}
	
	canit ();
	_zperr_ ("Interrupted\n");
	closeit ();
#ifdef	DEBUGZ
	_tout ("bibi: doing longjmp (_tohere, -1)\r\n");
#endif
	longjmp (_tohere, -1);
	}	/* static void bibi (int n) */
#endif


/****************************************************************************
*	_getfile																*
*	Receives file(s).  Returns 0 if successful.								*
****************************************************************************/

int _getfile (int s, char protocol, char *options, char *filename,
			  void (*reporter) (int type, void *data))
	{
	char *cp;
	int npats;
	char **patts;
	int exitcode = 0;
#if	0
	void (*sigint_sav) (int);
	void (*sigterm_sav) (int);
#endif

	/* Initialise global variables. */

	_z_socket = s;
	Batch = 0;
	_do_report = _zperr;
	Filcnt = 0;
	Lleft = 0;
	MakeLCPathname = TRUE;
	Nflag = 0;
	nocommand = FALSE;
	_Nozmodem = 0;
	_Quiet = 0;
	Readnum = HOWMANY;
	Rxascii = FALSE;
	Rxbinary = FALSE;
	_Rxtimeout = 100;
	_sending = FALSE;
	tryzhdrtype = ZRINIT;
	_Verbose = 0;
	_Wcsmask = 0377;
	_Zmodem = 0;
	_Zrwindow = 1400;

	chkinvok (protocol);					/* choose protocol				*/
	npats = 0;

	/* Parse options. */

	cp = options;
	while (*cp != '\0')
		{
		switch (*cp++)
			{
			case '7':						/* strip top bit				*/
				_Wcsmask = 0177;

			case 'a':						/* ASCII transfer				*/
				Rxascii = TRUE;
				break;

			case 'b':						/* binary transfer				*/
				Rxbinary = TRUE;
				break;

			case 'c':						/* XMODEM/CRC					*/
				_Crcflg = TRUE;
				break;

			case 'D':						/* fake file transfer			*/
				Nflag = TRUE;
				break;

			case 'i':						/* disallow remote command		*/
				nocommand = TRUE;
				break;
				
			case 'e':						/* escape ctl chars				*/
				_Zctlesc = 1;
				break;

			case 'p':						/* 'protect' option				*/
				_Lzmanag = ZMPROT;
				break;

			case 'q':						/* quiet						*/
				_Quiet = TRUE;
				_Verbose = 0;
				break;

			case 't':						/* change timeout				*/
				sscanf (cp, "%d", &_Rxtimeout);
				if (_Rxtimeout < 10 || _Rxtimeout > 1000)
					return (ERROR);

				cp = _stbnb (cp);
				break;

			case 'w':						/* window size					*/
				sscanf (cp, "%d", &_Zrwindow);
				cp = _stbnb (cp);
				break;

			case 'u':						/* no lower case convert		*/
				MakeLCPathname = FALSE;
				break;
				
			case 'v':						/* debug info					*/
				++_Verbose;
				break;

			default:
				return (ERROR);
			}	/* switch (*cp++) */
			
		}	/* while (*cp != '\0') */

	/* Get filename. */
	
	if (filename != NULL)
		{
		npats = 1;
		patts = &filename;
		}

	if (npats > 1)
		return (ERROR);
		
	if (Batch && npats)
		return (ERROR);

	/* Get reporter function */

	if (reporter != NULL)
		_do_report = reporter;				/* user supplied fn				*/

#if	0
	if (_Verbose)
		{
		if ((_zperr_handle = open (LOGFILE,
								   O_CREAT | O_APPEND | O_WRONLY | O_TEXT,
								   S_IFREG | S_IWRITE)) == -1)
			{
			char buff[80];
			
			sprintf (buff, "Can't open log file %s: %s\n",
					 LOGFILE, strerror (errno));
			(*_do_report) (2, buff);
			(*_do_report) (3, &Filcnt);		/* report no of files			*/
			return (ERROR);
			}
			
		_do_report = _zperr;				/* use default reporter			*/
		}
#endif

	if (!_Quiet)
		{
		if (_Verbose == 0)
			_Verbose = 2;

		}

#if	0
	if ((sigint_sav = signal (SIGINT, bibi)) == SIG_IGN)
		signal (SIGINT, SIG_IGN);
	else
		signal (SIGINT, bibi);

#endif

#if	0
	sigint_sav = signal (SIGINT, bibi);
	sigterm_sav = signal (SIGTERM, bibi);
#endif
		
	/* Set up escape routes. */

	if (setjmp (_tohere) != 0)
		{
#ifdef	DEBUGZ
		_tout ("getfile: exiting via _tohere jump\r\n");
#endif
		(*_do_report) (3, &Filcnt);			/* report files transferred		*/
#if	0
		signal (SIGINT, sigint_sav);
		signal (SIGTERM, sigterm_sav);
		(void) close (_zperr_handle);
#endif
		return (-2);						/* user abort					*/
		}

	if (setjmp (_nocarrier) != 0)
		{
#ifdef	DEBUGZ
		_tout ("getfile: exiting via _nocarrier jump\r\n");
#endif
		(*_do_report) (3, &Filcnt);			/* report files transferred		*/
#if	0
		signal (SIGINT, sigint_sav);
		signal (SIGTERM, sigterm_sav);
		(void) close (_zperr_handle);
#endif
		return (-3);						/* lost carrier					*/
		}

	/* Get the file(s). */
	
	if (wcreceive (npats, patts) == ERROR)
		{
		exitcode = 0200;
		canit ();
		}
		
	if (exitcode && !_Zmodem)			/* Bellow again with all thy might. */
		canit ();
		
	if (!_Quiet)
		_say ("\n");

	(*_do_report) (3, &Filcnt);			/* report files transferred			*/
#if	0
	signal (SIGINT, sigint_sav);
	signal (SIGTERM, sigterm_sav);
	(void) close (_zperr_handle);
#endif
	return (exitcode);
	}	/* int _getfile (int s, char protocol, char *options, char *filename,
			 void (*reporter) (int type, void *data)) */
			 

/****************************************************************************
*	wcreceive																*
*	Let's receive something already.										*
****************************************************************************/

#if 0
static char *rbmsg = "Ready to receive.\n"
					 "To begin transfer, issue the send command "
					 "to your modem program\r\n";
#endif

static int wcreceive (int argc, char **argp)
	{
	int c;

	if (Batch || argc == 0)
		{
		/* ZMODEM / YMODEM BATCH */
		
		_Crcflg = (_Wcsmask == 0377);
#if 0
		if (!_Quiet)
			_say (rbmsg);

#endif
		
		if ((c = tryz ()) != 0)
			{
			if (c == ZCOMPL)
				return OK;
				
			if (c == ERROR)
				goto fubar;
				
			c = rzfiles ();
			if (c)
				goto fubar;
				
			}	/* if ((c = tryz ()) != 0) */
		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;
					
				}	/* for (;;) */
				
			}	/* else */
			
		}	/* if (Batch || argc == 0) */
	else
		{
		/* XMODEM */
		
		Bytesleft = DEFBYTL;
		Filemode = 0;
		Modtime = 0L;

		procheader ("");
		strcpy (Pathname, *argp);
		checkpath (Pathname);
		(*_do_report) (0, Pathname);
		_say ("\nReady to receive %s\r\n", Pathname);
		if ((fout = open (Pathname,
						  O_CREAT | O_TRUNC | O_WRONLY | O_BINARY,
						  S_IFREG | S_IWRITE)) == -1)
			return ERROR;
			
		if (wcrx () == ERROR)
			goto fubar;

		++Filcnt;
		}	/* else */
		
	return OK;

	/* Transfer failed - clean up. */
	
fubar:
	canit ();
	if (fout != -1)
		close (fout);

	return ERROR;
	}	/* static int wcreceive (int argc, char **argp) */
	

/****************************************************************************
*	wcrxpn																	*
*	Fetch a pathname from the other end as a C ctyle ASCIZ string.  Length	*
*	is indeterminate as long as less than rz_blklen.  A null string 		*
*	represents no more files (YMODEM).										*
****************************************************************************/

static int wcrxpn (char *rpn)
	{
	int c;

	purgeline ();

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;
		}	/* while ((c = wcgetsec (rpn, 100)) != 0) */
		
	_sendline (ACK);
	return OK;
	}	/* static int wcrxpn (char *rpn) */


/****************************************************************************
*	wcrx																	*
*	Adapted from CMODEM13.C, written by Jack M Wierda and Roderick W Hart.	*
****************************************************************************/

static int wcrx (void)
	{
	int sectnum, sectcurr;
	char sendchar;
	int cblklen;						/* bytes to dump this block			*/

	_firstsec = TRUE;
	sectnum = 0;
	Eofseen = FALSE;
	sendchar = (char) (_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 > rz_blklen ? rz_blklen : (int) 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;
			}

		}	/* for (;;) */
		
	}	/* static int wcrx (void) */
	

/****************************************************************************
*	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.  <maxtime> 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).				*
****************************************************************************/

static int wcgetsec (char *rxbuf, int maxtime)
	{
	int checksum, wcj, firstch;
	unsigned short oldcrc;
	char *p;
	int sectcurr;

	for (_Lastrx = _errors = 0; _errors < RETRYMAX; _errors++)
		{
		if ((firstch = _readline (maxtime)) == STX)
			{
			rz_blklen = KSIZE;
			goto get2;
			}

		if (firstch == SOH)
			{
			rz_blklen = SECSIZ;

get2:
			sectcurr = _readline (1);
			if ((sectcurr + (oldcrc = _readline (1))) == _Wcsmask)
				{
				oldcrc = checksum = 0;
				for (p = rxbuf, wcj = rz_blklen; --wcj >= 0;)
					{
					if ((firstch = _readline (1)) < 0)
						goto bilge;
						
					oldcrc = updcrc (firstch, oldcrc);
					checksum += (*p++ = (char) 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;
						}

					}	/* if (_Crcflg) */
				else if (((checksum - firstch) & _Wcsmask) == 0)
					{
					_firstsec = FALSE;
					return sectcurr;
					}
				else
					_zperr_ ( "Checksum");

				}	/* if ((sectcurr + (oldcrc = _readline (1))) == _Wcsmask) */
			else
				_zperr_ ("Sector number garbled");

			}	/* if (firstch == SOH) */
		else if (firstch == EOT && Lleft == 0)	/* make sure eot really is
												   eot and not just mixmash	*/
			return WCEOT;
		else if (firstch == CAN)
			{
			if (_Lastrx == CAN)
				{
				_zperr_ ("Sender CANcelled");
				return ERROR;
				}
			else
				{
				_Lastrx = CAN;
				continue;
				}

			}	/* else if (firstch == CAN) */
		else if (firstch == ZTIMEOUT)
			{
			if (_firstsec)
				goto humbug;

bilge:
			_zperr_ ( "TIMEOUT");
			}
		else
			_zperr_ ( "Got 0%o sector header", firstch);

humbug:
		_Lastrx = 0;
		while (_readline (1) != ZTIMEOUT)
			;

		if (_firstsec)
			{
			_sendline (_Crcflg ? WANTCRC : NAK);
			Lleft = 0;					/* Do read next time ...			*/
			}
		else
			{
			maxtime = 40;
			_sendline (NAK);
			Lleft = 0;					/* Do read next time ...			*/
			}

		}	/* for (_Lastrx = _errors = 0; _errors < RETRYMAX; _errors++) */
		
	/* try to stop the bubble machine. */
	
	canit ();
	return ERROR;
	}	/* static int wcgetsec (char *rxbuf, int maxtime) */
	

/****************************************************************************
*	_readline																*
*	This version of _readline is reasonably well suited for reading many 	*
*	characters (except, currently, for the Regulus version!).  Timeout is	*
*	in tenths of seconds.													*
****************************************************************************/

int _readline (int timeout)
	{
	int n;
	static char *cdq;			/* pointer for removing chars from linbuf	*/
	time_t start;						/* start for timeout				*/

	if (linbuf == 0)
		linbuf = mallocw (HOWMANY);
		
	if (--Lleft >= 0)
		{
		if (_Verbose > 8)
			{
			_say ("%02x ", *cdq & 0377);
			}

		return (*cdq++ & _Wcsmask);
		}

	n = timeout / 10;
	if (n < 2)
		n = 3;		

	time (&start);
	if (_Verbose > 5)
		_vfile ("Calling read: alarm=%d  Readnum=%d ", n, Readnum);
		
	_no_carrier ();
	while (1)
		{
		Lleft = _receive ((unsigned char *) (cdq = linbuf), Readnum);
		if (Lleft != 0)
			break;						/* got something					*/

		if (time (NULL) >= start + n)
			{
			_zperr_ ("ZTIMEOUT");
			return (ZTIMEOUT);
			}

		}
		
	if (_Verbose > 5)
		{
		_say ("Read returned %d bytes\n", Lleft);
		}

	if (Lleft < 1)
		return ZTIMEOUT;

	--Lleft;
	if (_Verbose > 8)
		{
		_say ("%02x ", *cdq & 0377);
		}

	return (*cdq++ & _Wcsmask);
	}	/* int _readline (int timeout) */


/****************************************************************************
*	purgeline																*
*	Purge the modem input queue of all characters.							*
****************************************************************************/

static void purgeline (void)
	{
	unsigned char c;
	
	Lleft = 0;
	while (_rdchk () > 0)
		_receive (&c, 1);
		
	}	/* static void purgeline (void) */


/****************************************************************************
*	procheader																*
*	Process incoming file information header.								*
****************************************************************************/

static int procheader (char *name)
	{
	int openmode;
	char *p;

	/* set default parameters and overrides */

	(*_do_report) (0, name);
	openmode = O_CREAT | O_TRUNC | O_WRONLY | O_BINARY;
	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 = O_APPEND | O_WRONLY | O_BINARY;

	/* ZMPROT check for existing file */
	
	if (_zmanag == ZMPROT && (fout = open (name, O_RDONLY, S_IREAD)) != -1)
		{
		close (fout);
		return ERROR;
		}

	Bytesleft = DEFBYTL;
	Filemode = 0;
	Modtime = 0L;

	p = name + 1 + strlen (name);
	if (*p)
		{
		/* file coming from Unix or DOS system */

		sscanf (p, "%ld%lo%o", &Bytesleft, &Modtime, &Filemode);
		if (Filemode & UNIXFILE)
			++Thisbinary;

		if (_Verbose)
			{
			_say ("Incoming: %s %ld %lo %o\n",
				  name, Bytesleft, Modtime, Filemode);
			}
			
		}	/* if (*p) */
	else
		{
		/* File coming from CP/M system */
		
		for (p = name; *p; ++p)			/* change / to _ 					*/
			if ( *p == '/')
				*p = '_';
				
		if ( *--p == '.')				/* zap trailing period 				*/
			*p = 0;
			
		}	/* else */

	if (!_Zmodem && MakeLCPathname && !IsAnyLower (name))
		uncaps (name);
		
	strcpy (Pathname, name);
	if (_Verbose)
		{
		_say ("Receiving %s %s\n",
			  name, Thisbinary ? "BIN" : "ASCII");
		}
		
	checkpath (name);
	if (Nflag)
		name = "nul";					/* send to bit bucket				*/

	if ((fout = open (name, openmode, S_IFREG | S_IWRITE)) == -1)
		{
		_vfile ("Can't open %s mode %x, %s\n",
				name, openmode, strerror (errno));
		return ERROR;
		}
		
	return OK;
	}	/* static int procheader (char *name) */
	

/****************************************************************************
*	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.															*
****************************************************************************/

static int putsec (char *buf, int n)
	{
	char *p, *q;
	char locbuf[KSIZE + 1];
	int qcount;							/* no of chars in local buffer		*/
	
	if (Thisbinary)
		{
		write (fout, buf, n);
#if	0
		for (p = buf; --n >= 0;)
			putc (*p++, fout);

#endif		
		}
	else
		{
		if (Eofseen)
			return OK;

		q = locbuf;
		qcount = 0;
		for (p = buf; --n >= 0; ++p)
			{
#if	0
			/* We'll keep CR under MesS-DOS. */
			
			if (*p == '\r')
				continue;

#endif
			if (*p == CPMEOF)
				{
				Eofseen = TRUE;
				return OK;
				}

			*q++ = *p;					/* put byte in write buffer			*/
			qcount++;
#if	0
			putc (*p ,fout);
#endif
			}	/* for (p = buf; --n >= 0; ++p) */

		if (qcount != 0)
			write (fout, locbuf, qcount);
			
		}	/* else */
		
	return OK;
	}	/* static int putsec (char *buf, int n) */
	

/****************************************************************************
*	uncaps																	*
*	Make string <s> lower case.												*
****************************************************************************/

static void uncaps (char *s)
	{
	for (; *s; ++s)
		if (isupper (*s))
			*s = tolower (*s);
			
	}	/* static void uncaps (char *s) */


/****************************************************************************
*	IsAnyLower																*
*	IsAnyLower returns TRUE if string s has lower case letters.				*
****************************************************************************/

static int IsAnyLower (char *s)
	{
	for (; *s; ++s)
		if (islower (*s))
			return TRUE;
			
	return FALSE;
	}	/* static int IsAnyLower (char *s) */


/****************************************************************************
*	canit																	*
*	Send cancel string to get the other end to shut up.						*
****************************************************************************/

static void canit (void)
	{
#ifdef	DEBUGZ
	_tout ("canit: calling _canit ()\r\n");
#endif
	_canit ();
	Lleft = 0;							/* Do read next time ... 			*/
#ifdef	DEBUGZ
	_tout ("leaving canit ()\r\n");
#endif
	}	/* static void canit (void) */


/****************************************************************************
*	report																	*
*	Print <sct>.															*
****************************************************************************/

static void report (int sct)
	{
	if (_Verbose > 1)
		_say ("%03d%c", sct, sct % 10 ? ' ' : '\r');
		
	}	/* static void report (int sct) */


/****************************************************************************
*	chkinvok																*
*	Set chosen protocol.													*
****************************************************************************/

static void chkinvok (char protocol)
	{
	if (protocol == 'z')
		Batch = TRUE;
		
	if (protocol == 'y')
		Batch = _Nozmodem = TRUE;
		
	}	/* static void chkinvok (char protocol) */
	

/****************************************************************************
*	checkpath																*
*	Totalitarian Communist pathname processing.								*
****************************************************************************/

static void checkpath (char *name)
	{
	/* No restrictions in this version. */

	name = name;						/* silence compiler					*/
	}	/* static void checkpath (char *name) */


/****************************************************************************
*	tryz																	*
*	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.							*
****************************************************************************/

static int tryz (void)
	{
	int c, n;
	int 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 ZTIMEOUT:
				continue;

			case ZFILE:
				_zconv = _Rxhdr[ZF0];
				_zmanag = _Rxhdr[ZF1];
				_ztrans = _Rxhdr[ZF2];
				tryzhdrtype = ZRINIT;
				c = _zrdata (secbuf, KSIZE);
				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
						{
						if (nocommand)
							_stohdr (0L);	/* fake command ok				*/
						else
							_stohdr ((long) sys2 (secbuf));

						}

					purgeline ();		/* dump impatient questions 		*/
					do
						{
						_zshhdr (ZCOMPL, _Txhdr);
						} while (++_errors < 20 && _zgethdr (_Rxhdr,1) != ZFIN);
						
					ackbibi ();
					if (cmdzack1flg & ZCACK1)
						if (nocommand)
							longjmp (_tohere, -1);	/* don't do it			*/
						else
							exec2 (secbuf);

					return ZCOMPL;
					}	/* if (_zrdata (secbuf, KSIZE) == GOTCRCW) */
					
				_zshhdr (ZNAK, _Txhdr);
				goto again;
				
			case ZCOMPL:
				goto again;

			default:
				continue;

			case ZFIN:
				ackbibi ();
				return ZCOMPL;

			case ZCAN:
				return ERROR;
			}	/* switch (_zgethdr (_Rxhdr, 0)) */
			
		}	/* for (n = _Zmodem ? 15 : 5; --n >= 0;) */
		
	return 0;
	}	/* static int tryz (void) */
	

/****************************************************************************
*	rzfiles																	*
*	Receive one or more files with the ZMODEM protocol.						*
****************************************************************************/

static int rzfiles (void)
	{
	int c;

	for (;;)
		{
		switch (c = rzfile ())
			{
			case ZEOF:
			case ZSKIP:
				switch (tryz ())
					{
					case ZCOMPL:
						return OK;
						
					default:
						return ERROR;

					case ZFILE:
						break;
					}
	
				continue;

			default:
				return c;

			case ERROR:
				return ERROR;
			}	/* switch (c = rzfile ()) */
			
		}	/* for (;;) */
		
	}	/* static int rzfiles (void) */
	

/****************************************************************************
*	rzfile																	*
*	Receive a file with ZMODEM protocol.  Assumes file name frame is in 	*
*	secbuf.																	*
****************************************************************************/

static int rzfile (void)
	{
	int c, n;
	long rxbytes;
	int sent_zm = FALSE;				/* TRUE - sent ZMODEM string		*/
	Eofseen = FALSE;
	if (procheader (secbuf) == ERROR)
		{
		return (tryzhdrtype = ZSKIP);
		}
		
	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 ZTIMEOUT:
				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");
				++Filcnt;
				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)
					{
					(*_do_report) (1, &rxbytes);
					if (!sent_zm)
						{
						sent_zm = TRUE;
						_say ("ZMODEM%s", _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 ZTIMEOUT:
						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;
					}	/* switch (c = _zrdata (secbuf, KSIZE)) */
					
			}	/* switch (c = _zgethdr (_Rxhdr, 0)) */

		}	/* for (;;) */
		
	}	/* static int rzfile (void) */


/****************************************************************************
*	zmputs																	*
*	Send a string to the modem, processing for \336 (sleep 1 sec) and \335 	*
*	(break signal).															*
****************************************************************************/

static void zmputs (char *s)
	{
	int c;

	while (*s)
		{
		switch (c = *s++)
			{
			case '\336':
				sleep (1);
				continue;
				
			case '\335':
				_sendbrk ();
				continue;
				
			default:
				_sendline (c);
			}	/* switch (c = *s++) */
			
		}	/* while (*s) */
		
	}	/* static void zmputs (char *s) */


/****************************************************************************
*	closeit																	*
*	Close the receive dataset, return OK or ERROR.							*
****************************************************************************/

static int closeit (void)
	{
	if (close (fout) == -1)
		{
		_zperr_ ("file close ERROR\n");
		return ERROR;
		}
		
#if	0
	/* Bit fancy for MesS-DOS, what? */
	
	if (Modtime)
		{
		timep[0] = time (NULL);
		timep[1] = Modtime;
		utime (Pathname, timep);
		}

	if (Filemode)
		chmod (Pathname, (07777 & Filemode));
#endif

	return OK;
	}	/* static int closeit (void) */


/****************************************************************************
*	ackbibi																	*
*	Ack a ZFIN packet, let bygones be bygones.								*
****************************************************************************/

static void ackbibi (void)
	{
	int 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 ZTIMEOUT:
			default:
				break;
			}	/* switch (_readline (100)) */
			
		}	/* for (n = 3; --n >= 0;) */
		
	}	/* static void ackbibi (void) */
	

/****************************************************************************
*	sys2																	*
*	Strip leading ! if present, do shell escape.							*
****************************************************************************/

static int sys2 (char *s)
	{
	if (*s == '!')
		++s;
		
	return system (s);
	}	/* static int sys2 (char *s) */
	

/****************************************************************************
*	exec2																	*
*	Strip leading '!' if present and do an exec.							*
****************************************************************************/

static void exec2 (char *s)
	{
	char *comspec;						/* -> command.com					*/
	int result;							/* result of exec					*/
	
#ifdef	DEBUGZ
	_vfile ("exec2: exec %s\n", s);
#endif
	if (*s == '!')
		++s;

	if ((comspec = getenv ("COMSPEC")) != NULL)
		result = execl (comspec, "command", "/c", s, NULL);
	else
		result = execl ("command", "command", "/c", s, NULL);

	if (result == -1)
		perror ("exec2");
		
	}	/* static void exec2 (char *s) */
/* End of rz.c */
