#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "terminal.h"

/*

 intially written by Michael O'Reilly
 *seriously* bashed about by by quarters
 hell, I wonder if diff would find more than 5 matching lines anymore...
 jefftep@cs.utexas.edu
 jeff grills

 to use, run remotely like:   bovina% linecheck 2> remote.output
             locally:    linecheck < /dev/modem > /dev/modem 2> local.output 
	
 hack to be equivalent in your preferred shell.

 if it says something needs escaped, that means it didn't get through okay
 this time. if you get an invalid packet printed, it means the packet wasn't
 sent by the linecheck on the other side, and may either be line static,
 or some very braindead terminal response to a (possibly series) of characters
 to what was printed over the line.  in this case, it's your responsibilty
 to determine which, and escape the previously sent char if needed.  There is
 no way this program can identity a braindead terminal server from line static,
 so this is the way it has to be.

 if, for some reason, you get stuck out in lala land, and can't kill the program,
 try typing "^jexit^j".  That should kill it, and restore your terminal.

 It'll print "### sending char" and "### received valid".  Don't worry if these
 two number are out of sync.  That's fine.  Just worry, on either side, if you
 get some "Invalid packet: " lines.  Look at them closely, and see if it's line
 static, or a real problem.

 At the end, it'll print out a summary of what it thinks you should escape.
 This just means these chars didn't get recieved correctly this time.  Again,
 if line static munched something, some of these may be valid. 

 *** IF *** your terminal server generates extra responses for odd chars,
 then you may not be told to escape something, but need to anyway.  This will
 be evident from a "Invalid packet: " on the local side, after attempting to
 send a character.  Again, it may be line static. You have to make the call.

 if you're running it locally in a xterm, I suggest you turn on logging.

 if you have problems with this program, and want me to look at it, mail me
 *both* the local and remote output, and label them appropriately.

programmers notes:

 hopefully, soon, I'll add the ability to skip chars to this program, so you can
 test out the escapes you want.

 maybe do a fork() and process the two sides independently, so it never hangs.
 would cause minor quitting problems, but may be worth it.

 */

#define START_AT 0
#define STOP_AT  256

#define START      'A'
#define STOP       'B'
#define STOP_XON   'C'
#define XON        '\021'
#define BAIL       "exit"
#define QUIT       "quit"
#define TRIES      1
#define BUMS       1

#define BUFF_SIZE  200

/* -------------------------------------------------- */

char buff[BUFF_SIZE];
int valid[STOP_AT+1];

/* -------------------------------------------------- */

void debug(char *s)
{
  while (*s)
  {
    if ( isprint(*s) )
      fprintf(stderr, "%c", *(s++));
    else      
      fprintf(stderr, "<%d>", (int) *(s++));
  }
  fprintf(stderr, "\t");
}

/* -------------------------------------------------- */

void bum(char *s)
{
  int i;
	for (i=0; i<BUMS; i++)
	{
		if ( s == NULL )
			printf("\n");
		else
			printf("%s\n", s);
		sleep(1);
	}
}

/* -------------------------------------------------- */

void check(char *s)
{
  int k;
  char *t;
  t = s;

	/* convert int to number */
	k = 0;
	while ( isdigit(*s) )
		k = (k * 10) + (*(s++) - '0');
	
	/* verify this is a valid packet */
  /* printf("%d %c%c%c%c%c\n", i, START, (char)i, STOP, XON, STOP_XON ); */
	if ( (s[0] == ' ') && (s[1] == START) && (s[2] == (char)k) && (s[3] == STOP) )
	{
		fprintf(stderr, "%3d Received valid\n", k);
		valid[k] = 1;
	}
	else
	{
		fprintf(stderr, "Invalid packet: ");
		debug(t);
	}
}

/* -------------------------------------------------- */

int handle_incoming(void)
{
	
	/* get until non-empty */
	while( strlen(fgets(buff, 200, stdin)) == 0);

	if (strcmp(buff,BAIL) == 0)
	{
		terminal_restore(0);
		exit(0);
	}

	/* see if this is a quit */
	if (strcmp(buff,QUIT) == 0)
		return 1;
	
	check(buff);
	return 0;
}

/* -------------------------------------------------- */

void handshake()
{
  int i;

	fprintf(stderr, "Handshaking\n");

	printf("\n%c\n", START);
	i = 0;
	while ( ! i )
	{
		fgets(buff,200, stdin);
		if ( ( buff[0] == START) && ( buff[1] == '\0' ) )
		{
			i = 1;
			printf("\n%c\n", STOP);
		}
		if ( ( buff[0] == STOP) && ( buff[1] == '\0' ) )
			i = 1;
	}

	fprintf(stderr, "Handshaking sucessful\n");

}

/* -------------------------------------------------- */

void print_esc(void)
{
  int i, j;

  /* make printing at the end easier by making it more generic */
	valid[STOP_AT] = 1;

  /* print out the valid list */
  j = -1;
	fprintf(stderr, "\n");
  for (i = START_AT; i <= STOP_AT; i++)
		if ( (valid[i]) && (j != -1) )
		{
			if ((j <= 128) && (i == STOP_AT))
			{
				fprintf(stderr, "sevenbit\n");
				if ( j < 127 )
					fprintf(stderr, "escape %d-%d\n", j, 127);
				if ( j == 127 )
					fprintf(stderr, "escape 127\n");
			}
			else
				if (j == (i-1))
					fprintf(stderr, "escape %d\n", j);
				else
					fprintf(stderr, "escape %d-%d\n", j, i-1);
			j = -1;
		}
		else
			if ((!valid[i]) && (j == -1))
				j = i; 

}

/* -------------------------------------------------- */

void main(void)
{
  int i, k, quit;
	
	quit = 0;

  /* nothing has gotten through okay yet */	
  for (i = START_AT; i < STOP_AT; i++)
		valid[i] = 0;

  /* don't send this in a packet, cause it terminates a packet */
  valid[10] = -1;

	terminal_save(0);
	terminal_raw(0);
	
  handshake();

  /* test all the chars */
  for (i = START_AT; i < STOP_AT; i++ )
  {
		/* don't send chars we are told to skip */
    if ( valid[i] == -1 )
		{
			if ( i == 10 )
				valid[i] = 1;
      continue;
		}
		
    fprintf(stderr, "%3d sending char\n", i);

    /* attempt to send this char across, in a cute little packet */
		for (k=0; k<TRIES;k++)
		{
			printf("\n%d %c%c%c%c%c\n", i, START, (char)i, STOP, XON, STOP_XON );
			
			bum(NULL);

 			/* while we're at it, take a look to the other side */
			if (!quit)
				quit = handle_incoming();
		}
	}

  bum(QUIT);

  /* let the other side finish */
	while (!quit)
		quit = handle_incoming();
	
  print_esc();
	
	terminal_restore(0);
}


