/****************************************************************************
*	Author		:	G Todd													*
*	Language 	:	Turbo C 2.0												*
*	Logfile		:	cu.c													*
*	Project		:	Comms library.											*
*	Date 		:	19 Jan 90												*
*	Revision 	:	1.1		GT	Originate.									*
*****************************************************************************
*	Purpose		:	Testbed/demo for comms library.							*
*				:	Copyright 1990 Giles Todd.								*
****************************************************************************/

#include	<stdio.h>
#include	<stdlib.h>
#include	<conio.h>
#include	<dos.h>
#include	<string.h>
#include	<signal.h>
#include	"zmnp.h"

typedef enum { FALSE, TRUE } BOOLEAN;
typedef unsigned char BYTE;
typedef unsigned short int WORD;

#ifdef	__TURBOC__
extern unsigned _stklen = 16384;			/* big stack					*/
#endif


/****************************************************************************
*	Function prototypes.													*
****************************************************************************/

int main (int argc, char *argv[]);
static void usage (char *progname);
static void send_char (int c);
static void badarg (char *progname, char *arg);
static void mnperror (int errcode);
static void send_files (void);
static void send_command (void);
static void get_files (void);
#ifdef __TURBOC__
static void openwindow (void);
static void reporter (int type, void *data);
void tout (char *s);
static void closewindow (void);
#endif


/****************************************************************************
*	Global and static data.													*
****************************************************************************/

static BOOLEAN mnp_on = FALSE;			/* TRUE = MNP running				*/
static char z_down[] = "**\030B00000000000000\r\n\021";
static char hst_mnp_off[] = "AT&M0\r";	/* HST 'no MNP' string				*/
static BYTE rx_buf[BUFSIZ];				/* Receive buffer.					*/


/****************************************************************************
*	Program code.															*
****************************************************************************/

int main (int argc, char *argv[])
	{
	int rate = 5;						/* 2400 bps							*/
	int data = 8;						/* 8 data bits						*/
	int parity = 0;						/* no parity						*/
	int stop = 1;						/* 1 stop bit						*/
	int port = 1;						/* COM1								*/
	int handshake = 1;					/* RTS/CTS							*/
	unsigned short tx_free;				/* free in Tx buffer				*/
	unsigned short rx_rdy;				/* ready in Rx buffer				*/
	int c;								/* for keyboard input				*/
	BOOLEAN esc_flag = FALSE;			/* TRUE: last char was '~'			*/
	int i;								/* loop counter						*/
	union REGS regs;					/* for ioctl call					*/
	int mnpmode = -1;					/* -1 = no MNP, 0 = orig, 1 = ans	*/
	struct link_stat_blk mnpstat;		/* MNP status						*/
	long speed;							/* line speed						*/
	int zmatch = 0;						/* match count for z_down string	*/
	void (*sig_ptr) (int);				/* old SIGINT handler				*/
	
	/* Set 'raw' mode for stdin and stdout. */

	for (i = 0; i < 2; i++)
		{
		regs.x.ax = 0x4400;				/* get device info					*/
		regs.x.bx = i;					/* handle							*/
		intdos (&regs, &regs);
		regs.x.dx |= 0x20;				/* set 'raw' mode					*/
		regs.x.ax = 0x4401;				/* set device info					*/
		regs.x.bx = i;
		intdos (&regs, &regs);
		}

	/* Look for options. */

	if (argc > 1)
		{
		for (i = 1; i < argc; i++)
			{
			if (argv[i][0] != '-')
				badarg (argv[0], argv[i]);

			switch (argv[i][1])
				{
				case 'o':					/* MNP originate				*/
					mnpmode = 0;
					break;

				case 'a':					/* MNP answer					*/
					mnpmode = 1;
					break;

				case 'p':					/* COM port						*/
					sscanf (&argv[i][2], "%d", &port);
					if (port != 1 && port != 2)
						badarg (argv[0], argv[i]);

					break;

				case 's':					/* line speed					*/
					sscanf (&argv[i][2], "%ld", &speed);

					/* Select baud rate token from speed. Roll on ANSI C. */

					if (speed == 110L)
						rate = 1;
					else if (speed == 300L)
						rate = 2;
					else if (speed == 600L)
						rate = 3;
					else if (speed == 1200L)
						rate = 4;
					else if (speed == 2400L)
						rate = 5;
					else if (speed == 4800L)
						rate = 6;
					else if (speed == 9600L)
						rate = 7;
					else if (speed == 19200L)
						rate = 8;
					else if (speed == 38400L)
						rate = 9;
					else if (speed == 56000L)
						rate = 10;
					else if (speed == 115200L)
						rate = 11;
					else
						badarg (argv[0], argv[i]);

					break;
					
				default:
					badarg (argv[0], argv[i]);
				}	/* switch (argv[i][1]) */

			}	/* for (i = 1; i < argc, i++) */
			
		}	/* if (argc > 1) */
		
	/* Install isrs and init 8250. */

	c = connect (rate, data, parity, stop, port, handshake);
	if (c != 0)
		{
		switch (c)
			{
			case -64:
				fprintf (stderr, "Bad rate %d\n", rate);
				break;

			case -65:
				fprintf (stderr, "Bad data length %d\n", data);
				break;

			case -66:
				fprintf (stderr, "Bad parity %d\n", parity);
				break;

			case -67:
				fprintf (stderr, "Bad number of stop bits %d\n", stop);
				break;

			case -68:
				fprintf (stderr, "Bad port number %d\n", port);
				break;

			case -69:
				fprintf (stderr, "Bad handshake flag %d\n", handshake);
				break;

			default:
				fprintf (stderr, "Unknown error code %d\n", c);
				break;
			}	/* switch (c) */

		exit (c);
		}	/* if (c != 0) */

	/* Save SIGINT handler. */

	sig_ptr = signal (SIGINT, SIG_IGN);
	
	/* Switch off HST MNP */

	send ((unsigned char *) hst_mnp_off, strlen (hst_mnp_off));

	/* Main polling loop */

	while (1)
		{
		if (mnpmode != -1 && !mnp_on)
			{
			/* Attempt MNP connection when carrier detected. */

			if ((modemstat () & 0x80) != 0)
				{
				/* We have carrier - try to establish MNP link.*/

				c = mnpconnect (mnpmode);
				switch (c)
					{
					case 0:					/* success						*/
						mnp_on = TRUE;
						fputs ("MNP level 2\n", stderr);
						break;

					case -64:
						fputs ("MNP failure, unknown error\n", stderr);
						break;

					case -65:
						fputs ("MNP failure, timeout\n", stderr);
						break;

					case -66:
						fputs ("MNP failure, no carrier\n", stderr);
						break;

					case -70:
						fputs ("MNP failure, incompatible ARQ\n", stderr);
						break;

					case -72:
						fputs ("MNP failure, remote protocol error\n", stderr);
						break;

					default:
						fprintf (stderr, "Unknown error code %d\n", c);
						break;
					}	/* switch (c) */

				}	/* if ((modemstat () & 0x80) != 0) */

			}	/* if (mnpmode != -1 && !mnp_on) */

		/* Poll characters received from remote. */
		
		c = 0;
		if (mnp_on)							/* MNP connection?				*/
			{
			mnpstatus (&mnpstat);			/* get MNP status				*/
			if (mnpstat.r_count != 0)
				{
				c = mnpreceive (rx_buf, (mnpstat.r_count > sizeof (rx_buf))
										? sizeof (rx_buf)
										: mnpstat.r_count);
				if (c < 0)
					{
					mnperror (c);
					break;
					}
					
				for (i = 0; i < c; i++)
					putchar (rx_buf[i]);	/* and echo						*/

				}

			if (mnpstat.l_status == 0)
				{
				fputs ("MNP link broken\n", stderr);
				mnpdisconnect ();
				hangup ();
				mnp_on = FALSE;
				}
				
			}	/* if (mnp_on) */
		else
			{
			bufstat (&tx_free, &rx_rdy);	/* anything from remote?		*/
			if (rx_rdy != 0)
				{
				c = receive (rx_buf, (rx_rdy > sizeof (rx_buf))
									 ? sizeof (rx_buf)
									 : rx_rdy);
				for (i = 0; i < c; i++)
					putchar (rx_buf[i]);	/* and echo						*/
				
				}

			}

		/* Check for ZMODEM download. */

		for (i = 0; i < c; i++)
			{
			if (zmatch == strlen (z_down) - 1)
				{
				get_files ();				/* start ZMODEM download		*/
				zmatch = 0;
				}
				
			if (rx_buf[i] != z_down[zmatch])
				zmatch = 0;
			else
				zmatch++;

			}
			
			
		/* Poll user input. */
		
		if (kbhit () != 0)
			{
			c = getch ();					/* get user input				*/
				
			if (c == '~' && !esc_flag)
				{
				esc_flag = TRUE;			/* possible user escape			*/
				continue;
				}
				
			/* interpret any user escape */

			if (esc_flag)
				{
				esc_flag = FALSE;
				if (c == '.')
					break;					/* ~. means exit				*/

				switch (c)
					{
					case 'c':				/* remote command				*/
						send_command ();
						break;
						
					case 's':				/* send files					*/
						send_files ();
						break;

					case 'r':				/* receive files				*/
						get_files ();
						break;

					default:
						send_char ('~');	/* not escape sequence			*/
						send_char (c);
						break;
					}	/* switch (c) */
					
				}	/* if (esc_flag) */
			else
				send_char (c);				/* send data to remote 			*/
				
			}	/* if (kbhit () != 0) */

		}	/* while (1) */

	/* User wants to quit */

	if (mnp_on)
		mnpdisconnect ();
		
	disconnect ();							/* put back original state		*/

	signal (SIGINT, sig_ptr);
	return (0);								/* exit							*/
	}	/* int main (int argc, char *argv[]) */


/****************************************************************************
*	mnperror																*
*	Display MNP send/receive error message.									*
****************************************************************************/

static void mnperror (errcode)
	{
	switch (errcode)
		{
		case -64:
			fputs ("Link terminated, retransmission limit exceeded\n", stderr);
			break;

		case -65:
			fputs ("Link terminated, unable to send\n", stderr);
			break;

		case -66:
			fputs ("Link terminated, no carrier\n", stderr);
			break;

		case -67:
			fputs ("Link terminated, remote disconnect\n", stderr);
			break;

		default:
			fprintf (stderr, "Link terminated, unknown error %d\n", errcode);
			break;
		}	/* switch (errcode) */

	}	/* static void mnperror (errcode) */
	

/****************************************************************************
*	badarg																	*
*	There was a bad command line argument.  Print error message and exit.	*
****************************************************************************/

static void badarg (char *progname, char *arg)
	{
	fprintf (stderr, "Bad argument %s\n", arg);
	usage (progname);
	}	/* static void badarg (char *progname, char *arg) */
	

/****************************************************************************
*	send_char																*
*	Send a character to remote.												*
****************************************************************************/

static void send_char (int c)
	{
	int result;								/* from mnpsend					*/
	
	if (mnp_on)								/* MNP connection?				*/
		{
		while ((result = mnpsend ((unsigned char *) &c, 1)) != 1)
			{
			if (result < 0)
				{
				mnperror (result);
				break;
				}

			}

		}
	else
		{
		while (send ((unsigned char *) &c, 1) != 1)
			;

		}

	}	/* static void send_char (int c) */
	

/****************************************************************************
*	usage																	*
*	Display usage message and exit.											*
****************************************************************************/

static void usage (char *progname)
	{
	fprintf (stderr, "Usage: %s [options]\n"
					 "Options are:\n"
					 "-o		MNP originate mode\n"
					 "-a		MNP answer mode\n"
					 "-p<port>	Use COM<port> (default COM1)\n"
					 "-s<speed>	Bps (default 300)\n",
					 progname);
	exit (1);
	}	/* static void usage (char *progname) */


/****************************************************************************
*	send_files																*
*	Send file(s) via ZMODEM.												*
****************************************************************************/

static void send_files (void)
	{
	char *filenames[5];					/* max 4 files						*/
	int i;								/* loop counter						*/
	char s[65];							/* one filename						*/

	/* Gather filenames - wildcards not supported (use findfirst and findnext
	 * for wildcard support).
	 */
	 
	for (i = 0; i < 4; i++)
		{
		printf ("\nFilename (<enter> only when done): ");
		gets (s);
		if (strlen (s) == 0)
			{
			filenames[i] = "";
			break;
			}

		filenames[i] = malloc (strlen (s) + 1);
		strcpy (filenames[i], s);
		}

	putc ('\n', stdout);
	filenames[4] = "";					/* ensure last is ""				*/

	/* Send the file(s) using ZMODEM, binary xfer, 32 bit CRC, debug info,
	 * default reporting.
	 */

#ifdef __TURBOC__
	openwindow ();
	i = sendfile ('z', "by", filenames, reporter);
	closewindow ();
	switch (i)
#else
	switch (i = sendfile ('z', "by", filenames, NULL))
#endif
		{
		case 0:
			fputs ("File(s) sent ok\n", stderr);
			return;

		case -1:
			fputs ("Invalid ZMODEM option\n", stderr);
			return;

		case -2:
			fputs ("User abort\n", stderr);
			return;

		case -3:
			fputs ("No carrier\n", stderr);
			return;

		default:
			fprintf (stderr, "Unknown error code %d\n", i);
			return;
		}	/* switch (i = sendfile ('z', "by", filenames, NULL)) */
		
	}	/* static void send_files (void) */


/****************************************************************************
*	send_command															*
*	Send remote command via ZMODEM.											*
****************************************************************************/

static void send_command (void)
	{
	char s[132];						/* command text						*/
	int cmdlen;							/* command length					*/
	char *filenames[1];					/* dummy filenames parameter		*/
	
	/* Enter command. */

	printf ("Remote command: ");
	gets (s + 2);
	*s = 'i';							/* no wait for completion			*/
	s[1] = '#';							/* turn into option string			*/
	cmdlen = strlen (s);
	s[cmdlen] = '#';
	s[cmdlen + 1] = '\0';
	filenames[0] = "";					/* dummy filenames parameter		*/
	
	/* Send the command. */

#ifdef __TURBOC__
	openwindow ();
	cmdlen = sendfile ('z', s, filenames, reporter);
	closewindow ();
	switch (cmdlen)
#else
	switch (cmdlen = sendfile ('z', s, filenames, NULL))
#endif
		{
		case 0:
			fputs ("Command sent ok\n", stderr);
			return;

		case -1:
			fputs ("Invalid ZMODEM option\n", stderr);
			return;

		case -2:
			fputs ("User abort\n", stderr);
			return;

		case -3:
			fputs ("No carrier\n", stderr);
			return;

		default:
			fprintf (stderr, "Unknown error code %d\n", cmdlen);
			return;
		}	/* switch (cmdlen = sendfile ('z', "by", filenames, NULL)) */
	
	}	/* static void send_command (void) */


/****************************************************************************
*	get_files																*
*	Get file(s) using ZMODEM.												*
****************************************************************************/

static void get_files (void)
	{
	int i;
	
#ifdef __TURBOC__
	openwindow ();
	i = getfile ('z', "b", NULL, reporter);
	closewindow ();
	switch (i)
#else
	switch (i = getfile ('z', "b", NULL, NULL))
#endif
		{
		case 0:
			fputs ("File(s) received ok\n", stderr);
			return;

		case -1:
			fputs ("Invalid ZMODEM option\n", stderr);
			return;

		case -2:
			fputs ("User abort\n", stderr);
			return;

		case -3:
			fputs ("No carrier\n", stderr);
			return;

		default:
			fprintf (stderr, "Unknown error code %d\n", i);
			return;
		}	/* switch (i = sendfile ('z', "by", filenames, NULL)) */
		
	}	/* static void get_files (void) */


#ifdef __TURBOC__

static BYTE savescreen[61 * 5 * 2];		/* save area for screen window		*/
static struct text_info oldscr;			/* old coordinates etc				*/

/****************************************************************************
*	openwindow																*
*	Set up the window for the ZMODEM report function.						*
****************************************************************************/

void openwindow (void)
	{
	gettextinfo (&oldscr);				/* save old cursor etc				*/
	gettext (11, 11, 71, 15, savescreen);	/* save what's there			*/
	window (11, 11, 70, 15);			/* set window boundaries			*/
	clrscr ();
	window (11, 11, 71, 15);			/* while drawing box				*/
	gotoxy (1, 1);
	cprintf ("ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ»");
	gotoxy (1, 2);
	cprintf ("º                                                          º");
	gotoxy (1, 3);
	cprintf ("º                                                          º");
	gotoxy (1, 4);
	cprintf ("º                                                          º");
	gotoxy (1, 5);
	cprintf ("ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ¼");
	window (12, 12, 69, 14);			/* text boundaries					*/
	}	/* void openwindow (void) */


/****************************************************************************
*	closewindow																*
*	Restore screen to its previous state.									*
****************************************************************************/

void closewindow (void)
	{
	puttext (11, 11, 71, 15, savescreen);	/* put back what was there		*/
	window (oldscr.winleft, oldscr.wintop, oldscr.winright, oldscr.winbottom);
	gotoxy (oldscr.curx, oldscr.cury);
	}	/* void closewindow (void) */


/****************************************************************************
*	tout																	*
*	Display <s> in the current window, filtering line feeds.				*
****************************************************************************/

void tout (char *s)
	{
	int c;
	
	while ((c = *s++) != '\0')
		if (c != '\n')
			putch (c);

	}	/* void tout (char *s) */
	

/****************************************************************************
*	reporter																*
*	Do report functions for ZMODEM.											*
****************************************************************************/

void reporter (int type, void *data)
	{
	char buf[128];						/* for received text				*/
	
	switch (type)
		{
		case 0:							/* filename							*/
			gotoxy (1, 1);
			clreol ();
			sprintf (buf, "FILE: %s", data);
			tout (buf);
			return;

		case 1:							/* transfer count					*/
			gotoxy (1, 2);
			clreol ();
			sprintf (buf, "%7ld", *((long *) data));
			tout (buf);
			return;

		case 2:							/* other text						*/
			gotoxy (1, 3);
			clreol ();
			sprintf (buf, "%s", data);
			tout (buf);
			return;

		default:
			fprintf (stderr, "reporter: unknown type %d\n", type);
			return;
		}	/* switch (type) */

	}	/* void reporter (int type, void *data) */
#endif
