/* pe7pho.c */
/*
 *  P h o n e   File Transfer Utility
 *
 * usage:
 * phone -i -d -v -l -b -a -p -h -c
 *
 *  where   i=init modem <flag>, d=debug level <number>, v=verbose <flag>,
 *			l=line <device>, b=baud rate <number>, a=answer <flag>,
 *			p=phone number <string>, h=hang-up request <'a' or 'o'>,
 *			c=direct connect <'a' or 'o'>
 */

/*
 *  Modification History:
 *
 *  June 85	- Init write. Dan L. Eisner, Perkin-Elmer Corp.
 *
 */

#include <std.h>		/* Standard IDRIS definitions */
#include <sys.h>		/* Idris system subroutines */
/*#include "sgtty.h"		/* Special TTY speed definations */
#include "pe7tty.h"		/* Special TTY speed definations */

/* Symbol Definitions */

#define BUFSIZE 80		/* A buffer size for I/O */

#define MYTIME  10		/* Seconds after which I should be timed out */

#define TRUE	-1		/* Boolean constants */
#define FALSE   0

/* Global Variables */

int
	iflg {0},			/* Indicates to init the modem */
	aflg {0},			/* Indicates modem mode */
	vflg {0},			/* Indicates verbose mode */
	hflg {0},			/* Indicates to hang up the phone */
	cflg {0},			/* direct connection thru a modem flag */
	timint {0},			/* This is the present time out interval */
	debug {0};			/* indicates level of debugging output (0=none) */

char modem_data[BUFSIZE] {0};/* Temp buffer for modem command data */

FILE   ttyfd {0};		/* File descriptor of tty for I/O */

struct tty
		savemode {0},	/* tty saved mode */
		ttymode {0};	/* tty raw mode */
/*
 *  m a i n
 *
 *  Main routine - parse command and options, set up the
 *  tty lines, and dispatch to the appropriate routine.
 */

main(argc,argv)
int argc;				/* Character pointers to and count of */
char **argv;			/* command line arguments */

{
	char *ttyname,		/* tty name for LINE argument */
	*ph_num,			/* Phone number for PHONE argument */
	result_code,		/* The code the modem answered with */
	tmp[6];				/* Retry counter buffer */
	int speed;			/* speed of assigned tty, */
	int dial_count;		/* Number of times we have dialed the phone */
	int answer_count;   /* The number of times we tried to answer phone */
	int pflg;			/* A flag to set if there is a phone number */
	int pdial;			/* Pulse dialing flag */

		/* Make sure there's a command line */
	if (argc < 2)
		Usage("phone -i -d# -v -l/dev/tty# -b<rate> -a -p<tel. no> -h -c\n");

	iflg = FALSE;
	aflg = FALSE;
	hflg = FALSE;
	vflg = FALSE;
	cflg = FALSE;		/* Turn off all parse flags */
	ttyname = 0;		/* We did not get a line assignment (pointer) */
	ph_num = NULL;		/* Set the phone number pointer to a null */
	pflg = FALSE;		/* ... and the flag to FALSE */
	speed = 0;			/* Preset the speed to no change */
	debug = FALSE;		/* Turn off the debug mode */

	getflags(&argc,&argv, "i,a,v,h?,c?,l*,p*,b#,d#:F",
	&iflg, &aflg, &vflg, &hflg, &cflg, &ttyname, &ph_num, &speed, &debug);

	/* Done parsing */

	if (debug > 0)
		vflg = TRUE;		/* Force verbose during debug */

	pflg = (ph_num != 0);/* Set up a phone number flag */
	if (!aflg && !pflg && !hflg && !cflg && !iflg)
		Usage("We need to do something?");

	if ((aflg + pflg) > 1)
		Usage("Dial and answer the phone?");

	if (cflg && (aflg || pflg))
		Usage("Direct connect and dial at same time?");

	if (hflg != 0 && (aflg || pflg))
		Usage("Hang-up after connecting?");

	if (cflg == 'a' || cflg == 'A')
		aflg = TRUE; /* make it answer */

	if (cflg == 'o' || cflg == 'O')
		pflg = TRUE;		/* force the dial routine to work */

	if (aflg || pflg)
		iflg = TRUE;		/* force int of modem */

	if (ttyname == 0)		/* If LINE was not specified, we */
		ttyname = "/dev/lnk0";/* operate with link tty */

	ttyfd = open(ttyname, UPDATE, 0);/* Open the tty line */
	if (ttyfd < 0)
		Usage("Cannot open %s.", ttyname);
	
	/* Put the proper tty into the correct mode */
	egtty(ttyfd, &savemode);/* Save the inputed mode */
	egtty(ttyfd,&ttymode);  /* set for changing the setup */

	ttymode.t_mode |= (M_ALL|M_RAW|MR_XON);
	ttymode.t_mode &= ~(M_ECHO | M_2STOP);
	ttymode.t_min = 128;
	ttymode.t_time = 0;
	timint = 0;

	if (speed)		/* User specified a speed? */
	{
		switch(speed)   /* Get internal system code */
		{
		case 110: speed = B110; ttymode.t_mode &= M_2STOP; break;
		case 150: speed = B150; break;
		case 300: speed = B300; break;
		case 1200: speed = B1200; break;
		case 2400: speed = B2400; break;
		case 4800: speed = B4800; break;
		case 9600: speed = B9600; break; 
		default: Usage("Bad line speed.");
		}
		ttymode.t_ispeed = speed;
		ttymode.t_ospeed = speed;
	}

	estty(ttyfd, &ttymode);  /* Put asg'd tty in raw mode */

	if (debug)
	{
		printf("Main 1: A=%d, H=%d, D=%d, ", aflg, hflg, cflg);
		printf("L=%s, P=%s\n", ttyname, ph_num);
		printf("        Line speed to remote host is %d\n",speed);
		printf("Main 2: Bits for ttyfd %x\n",ttymode.t_mode);
	}

	/* All set up, now execute the command that was given. */

	if (iflg)		/* Init the modem */
	{
		timint = MYTIME / 2;
		ttymode.t_time = timint * 10;
		estty(ttyfd, &ttymode);  /* Put tty in short time mode */
		if (vflg) printmsg("Initializing the modem");
		if ((result_code = init_modem()) <= 0)
			Usage("Can not initilize the modem, return = %d\n",result_code);
	}
	if (pflg)
	{
		timint = 0;
		ttymode.t_time = 0;
		estty(ttyfd, &ttymode);  /* Put tty in long time mode */
  
		/* If the first character of the phone number is a p then set
			up pulse dialing. */
		if (*ph_num == 'p' || *ph_num == 'P')
		{
			pdial = TRUE;
			ph_num++;
		}
		else
			pdial = FALSE;	/* Set pulse dialing to FALSE */
		dial_count = 0;
		if (vflg) printmsg("Dialing %s", ph_num);
		do
		{
			if (dial_count++ >= 5)
				Usage("Dial count expired");
			modem_write(pdial ? "ATPD" : "ATTD", FALSE);
			if (!cflg)
				modem_write(ph_num,FALSE);
			modem_write("\r",FALSE);
			if(vflg)
				write(STDERR, &tmp, decode(&tmp, 3, "%2i\r", dial_count));
		}
		while ((result_code = get_result()) == '3');   /* keep dialing */
		
		if (result_code != '1' && result_code != '5')
		{
			init_modem();
			Usage("    \007ERROR, BAD RESULT CODE: 0x%x\r",result_code);
		}
		
		if (vflg)
			printmsg("Connect at %s\n",
				result_code == '5' ? "1200 Baud" : "300 Baud");
		sleep(2);			/* Wait for other side to be ready */
		timint = MYTIME;
		ttymode.t_time = timint * 10; /* Reset time out */
		estty(ttyfd, &ttymode);

	}		/* End of pflg */
	if (aflg)
	{
		answer_count = 0;
		if (!cflg)			/* Direct mode is off? */
		{
			timint = 30;		/* set the time to 30 sec. */
			ttymode.t_time = 50;/* Make the time out as big as I can */
			estty(ttyfd, &ttymode);
			if (vflg) printmsg("Waiting for ring");
			do
			{
				if (answer_count++ >= 10)
					Usage("Answer count expired.");
				if(vflg)
					write(STDERR, &tmp, decode(&tmp, 3, "%2i\r",
						answer_count));
			}
			/* wait for phone to ring (but count tries) */
			while ((result_code = get_result()) != '2');
			modem_write("ATS2=28A\r",FALSE);	/* Answer the phone */
			result_code = get_result();
		}
		else	/* !cflg */
		{
			timint = 0;
			ttymode.t_time = 0;	/* set time out */
			estty(ttyfd, &ttymode);
			if (vflg) printmsg("Waiting for connection");
			do
			{
				if (answer_count++ >= 5)
					Usage("Answer counter expired.");
				modem_write("ATS2=28A\r",FALSE); /* Answer the phone */
				if(vflg)
					write(STDERR, &tmp, decode(&tmp, 3, "%2i\r",
						answer_count));
			}
			while ((result_code = get_result()) == '3');

		}	/* end of else !cflg */
		if (result_code != '1' && result_code != '5')
			Usage("Modem failed to answer correctly.");
		if (vflg)
			printmsg("Answered at %s\n",
				result_code == '5' ? "1200 Baud" : "300 Baud");
		timint = MYTIME;
		ttymode.t_time = timint * 10;   /* Reset time out */
		estty(ttyfd, &ttymode);  /* Put tty in time out mode */
	}   /* end of aflg */
	if (hflg)
	{
		timint = MYTIME / 5;
		ttymode.t_time = timint * 10;	/* restore time out count */
		estty(ttyfd, &ttymode);
		if(vflg) printmsg("Hanging up");
		hang_modem();	/* Hang up the phone */
	}	/* end of else !hflg */

	estty(ttyfd, &savemode);
	exit(YES);
}

/*
 * p r i n t m s g
 *
 * printmsg - like printf with "Phone: " preappended
 */
printmsg(fmt, a1, a2, a3, a4, a5)
TEXT *fmt;
{
	char cp[BUFSIZE];
	cpystr(&cp, "Phone: ", fmt, "\n", NULL);
	printf(&cp, a1, a2, a3, a4, a5);
}

/*
 * U s a g e
 *
 * Print formatted message and the exit with a status of NO
 */
Usage(message, a1, a2, a3, a4, a5)
char *message;
{
	char cp[BUFSIZE];

	cpystr(&cp, "Phone usage: ", message, "\n", NULL);
	printf(&cp, a1, a2, a3, a4, a5);
	if (ttyfd > 0)
		estty(ttyfd, &savemode);	/* restore the tty */
	exit(NO);
}

/*
 * p r i n t f
 *
 * Print formatted output. Convert from UNIX to IDRIS
 */
printf(msg, a1, a2, a3, a4, a5)
char *msg;
{
	char t,
	*cpp,
	cp[BUFSIZE]; /* Line pointer for the reformatted string */

	cpp = cp;		/* Init the buffer pointer */

	while ((t = *cpp++ = *msg++) != NULL)
		if (t == '%')
			switch(t = *msg)
			{
			case 'd': *cpp++ = 'i';
				t = *msg++; /* Scrap character */
				break;
			case 'c': *cpp++ = 'a';
				*cpp++ = 'c';
				t = *msg++; /* Scrap character */
				break;
			case 'x': *cpp++ = 'h';
				*cpp++ = 'i';
				t = *msg++; /* Scrap character */
				break;
			case 's': *cpp++ = 'p';
				t = *msg++; /* Scrap character */
				break;
			case '\0': break;
			default: *cpp++ = *msg++;
			}
	putfmt(&cp, a1, a2, a3, a4, a5);
}

/*
 * i n i t _ m o d e m
 *
 * Init the modem. first try to reset then if not successful then try to
 *  escape the modem, and reset.
 * The routine returns a result of '0' to '5' or a negative number for timeout
 */
char init_modem()
{
	int tries;
	char tmp;

	if (debug) printf("Init_modem 1:\n");

	for (tries = 1; tries <= 4; tries++)
	{
		sleep(1);				/* Wait for a bit */
		if (tries >= 2)
		{
			modem_write("\34\34\34",FALSE);
			if ((tmp = get_result()) != '0')
			{
				modem_write("\35\35\35",FALSE);
				if ((tmp = get_result()) != '0')
				{
					modem_write("+++",FALSE);
					tmp = get_result();
				}
			}
		}
		modem_write("\rATZ\r", FALSE);
		if ((tmp = get_result()) == '0') break;
		if (debug) printf("Init_modem 2: tries = %d\n",tries);
	}
	if (tries > 4) return(tmp);

	sleep(2);	/* wait for a while while the modem resets */
	modem_write("AT E0 F1 Q0 V1 X1 S0=0 S5=255 S2=29 \r",TRUE);
	if (debug) printf("Init_modem exit\n");
	return(get_result());
}

/*
 * h a n g _ m o d e m
 *
 * Hang up the modem. First escape to the command mode, then hang up the
 * phone.
 * The routine returns a result of '0' or a negative number for timeout
 */
char hang_modem()
{
	int tries;
	char tmp;

	if (debug) printf("Hang_modem 1:\n");

	for (tries = 1; tries <= 4; tries++)
	{
		sleep(2);				/* Wait for a bit each time */
		modem_write((hflg == 'a' || hflg == 'A') ?
			"\34\34\34" : "\35\35\35", FALSE);
		if ((tmp = get_result()) != '0')
		{
			modem_write("\34\34\34",FALSE);
			if ((tmp = get_result()) != '0')
			{
				modem_write("\35\35\35",FALSE);
				if ((tmp = get_result()) != '0')
				{
					modem_write("+++",FALSE);
					tmp = get_result();
				}
			}
		}
		modem_write("ATH0\r", FALSE);
		if ((tmp = get_result()) == '0') break;
		if (debug) printf("Hang_modem 2: tries = %d\n",tries);
	}
	if (debug) printf("Hang_modem exit\n");
	return(tmp);
}
/*
 * g e t _ r e s u l t
 *
 * This routine finds out the result code from the modem.
 */
char get_result()
{
	int tmpx;	/* The status and goes here */
	char result;
	result = 0;	/* Reset the result flag */
	while (result == 0)  /* do till something comes in that we recognize */ 
	{
		if ((tmpx = modem_read(&modem_data)) <= 0)
			result = -1;
		else if (modem_data[substr(&modem_data, "OK")])
			result = '0';
		else if (modem_data[substr(&modem_data, "CONNECT 1200")])
			result = '5';
		else if (modem_data[substr(&modem_data, "RING")])
			result = '2';
		else if (modem_data[substr(&modem_data, "NO CARRIER")])
			result = '3';
		else if (modem_data[substr(&modem_data, "ERROR")])
			result = '4';
		else if (modem_data[substr(&modem_data, "CONNECT")])
			result = '1';
		if (debug)
			printf("Get_result 1: %c, %d, \"%s\"\n",result,tmpx,modem_data);
	}   /* End of while loop */
	if (debug)
		printf("Get_result 2: %c, %d, \"%s\"\n",result,tmpx,modem_data);
	return(result);
}
/*
 * m o d e m _ w r i t e
 *
 * send a string to the modem. If eat is TRUE, eat a single echo per xmitted
 * character. Does not account for echoed \n's after \r's. Current version
 * turns off command echo anyway, so no difference. 
 */

modem_write(s, eat)
char *s;
int eat;
{
	char t;
	int tmpx;
	tmpx = 1;
	while (*s)
	{
		write(ttyfd, s++, 1);
		if (eat)
			if ((tmpx = ioread(&t)) <= 0) break;
	}
	return(tmpx);
}
/*
 * m o d e m _ r e a d
 *
 * Modem read routine. This routine reads the returned control data from
 *  The modem.
 */
modem_read(t)
char *t;
{
	char chr;
	BYTES point;
	point = t;	/* Set pointer to start of the string */
	while (TRUE)
	{
		if (ioread(t) <= 0) break; /* If an error or eof then exit */
		if (*t == '\n')	/* What was the last character? */
		{
			*t++ = '\134'; *t++ = 'n';  /* Make the return into a \n */
			break;	/* If it was an EOL then exit */
		}
		if ((chr = *t) < ' ')
		{
			*t++ = '\134'; *t = chr + '@'; /* Make control to alpha */
		}
		t++;			/* Bump the pointer */
	}
	*t = NULL;	/* Make sure the string ends with a null */
	return(t - point);   /* Return a character counter */
}

/*
 *  i o r e a d
 *
 *  Read a character from the i/o channel
 */
ioread(t)
char *t;
{
ULONG time_end,
	  time();
int result;
	if (timint > 0)
	{
		time_end = time() + timint;
		while (time_end >= time())
			if ((result = read(ttyfd, t, 1)) > 0) break;
	}
	else
		result = read(ttyfd, t, 1);
	return(result);
}
/* pe7pho.c End-of-file */
