/*
 * This is Ping-Pong convers/conversd derived from the wampes
 * convers package written by Dieter Deyke <deyke@mdddhd.fc.hp.com>
 *
 * Modifications by Fred Baumgarten <dc6iq@insu1.etec.uni-karlsruhe.de>
 * $Revision: 2.99 $$Date: 1994/10/02 10:33:12 $
 */

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#if defined(_AIX) && defined(_IBMR2)
#include <time.h>
#include <sys/select.h>
#endif
#include <termios.h>
#include <unistd.h>

#ifdef linux
#include <sys/ioctl.h>
#endif

#if defined(__TURBOC__) || defined(__STDC__)
#define __ARGS(x)       x
#else
#define __ARGS(x)       ()
#define const
#endif

#if defined(mips)		/* no headerfiles for the whole stuff ? */
extern int select __ARGS((int nfsd, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout));
#endif

#if defined(mips) || defined(_AIX)	
extern void bzero __ARGS((char *b1, int length));
extern int getopt __ARGS((int argc, char **argv, char *optstring));
extern int socket __ARGS((int af, int type, int protocol));
extern int connect __ARGS((int s, struct sockaddr *name, int namelen));
extern int ioctl __ARGS((int d, int request, void *argp));
#endif

#if defined(hpux)
#define fd_set fd_mask		/* not sure about this... comments ? */
#endif

extern struct sockaddr *build_sockaddr __ARGS((const char *name, int *addrlen));
extern char *optarg;
extern int optind;

static int issue_running = 1;	/* ignore these Issue messages */
static char convtype[512];
static int outcnt = 0;
static char outbuf[20480];
static struct timeval sel_timeout;
static struct termios prev_termios;
static struct termios curr_termios;
static int echo;
static long tt;

#if defined(mips)	/* select(2) is not POSIX-compatible !!! */
#undef POSIX
#endif

#ifndef POSIX
static long tt2;
#endif

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

static void stop(arg)
char *arg;
{
  if (*arg) perror(arg);
  tcsetattr(0, TCSANOW, &prev_termios);
  exit(0);
}

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

void do_select_call()
{
  fd_set mask;
  int size;
  int i;
  int incnt = 0;
  char inbuf[20480];
  char buffer[20480];
  char c, *cp;

  FD_ZERO(&mask);
  FD_SET(0,&mask);
  FD_SET(3,&mask);
  sel_timeout.tv_sec = 0;
  sel_timeout.tv_usec = 200000;
  select(4, &mask, (fd_set *) 0, (fd_set *) 0, &sel_timeout);
  if (FD_ISSET(0,&mask)) {
    do {
      if ((size = read(0, buffer, sizeof(buffer))) <= 0) stop(convtype);
      for (i = 0; i < size; i++) {
	c = buffer[i];
	if (c == '\r') c = '\n';
	if (c == (char) prev_termios.c_cc[VERASE]) {
	  if (incnt) {
	    incnt--;
	    if (echo && write(1, "\b \b", 3) < 0) stop(convtype);
	  }
	} else if (c == (char) prev_termios.c_cc[VKILL]) {
	  for (; incnt; incnt--)
	    if (echo && write(1, "\b \b", 3) < 0) stop(convtype);
	} else if (echo && c == 18) {
	  if (write(1, "^R\n", 3) < 0) stop(convtype);
	  if (write(1, inbuf, incnt) < 0) stop(convtype);
	} else {
	  inbuf[incnt++] = c;
	  if (echo && write(1, &c, 1) < 0) stop(convtype);
	}
	if (c == '\n' || incnt == sizeof(inbuf) - 1) {
	  if (inbuf[0] == '!') {
	    if (inbuf[1] != '!') {
	      inbuf[incnt] = '\0';
	      if (tcsetattr(0, TCSANOW, &prev_termios)) stop(convtype);
	      system(inbuf + 1);
	      if (tcsetattr(0, TCSANOW, &curr_termios)) stop(convtype);
	      if (write(3, "\n", 1) < 0) stop(convtype);
	    } else {
	      if (write(3, inbuf+1, incnt-1) < 0) stop(convtype);
	    }
	  } else {
	    if (write(3, inbuf, incnt) < 0) stop(convtype);
	  }
	  incnt = 0;
#ifdef POSIX
	  fflush(NULL);
#endif
	}
      }
    } while (incnt);
  }
  if (FD_ISSET(3,&mask)) {
    size = read(3, buffer, sizeof(buffer));
    if (size <= 0) stop("");
    for (i = 0; i < size; i++) {
      c = buffer[i];
      if (c != '\r') outbuf[outcnt++] = c;
    }
#ifndef POSIX
    time(&tt);
#else
    fflush(NULL);
#endif
  }
#ifndef POSIX
  time(&tt2);
  tt2--;
#endif
  if (outcnt) {
#ifdef POSIX
    if ((sel_timeout.tv_sec == 0) && (sel_timeout.tv_usec == 0)) {
#else
    if (tt < tt2) {
#endif
      if (!issue_running) {
	if (write(1, outbuf, outcnt) < 0) stop("");
      } else {
        cp = outbuf;
	while (*cp) {
	  if (cp[0] != '*') {
	    issue_running = 0;
	    if (write(1, cp, outcnt) < 0) stop("");
	    break;
	  } else {
	    while (*cp != '\n') {
	      cp++;
	      outcnt--;
	    }
	    cp++;
	    outcnt--;
	  }
	}
      }
      outcnt = 0;
#ifdef POSIX
      fflush(NULL);
#endif
    }
  }
}

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

int main(argc, argv)
int argc;
char **argv;
{
  char *server = "localhost:3600";
  char *sp;
  int ch;
  int channel = 0;
  int errflag = 0;
  int addrlen;
  struct sockaddr *addr;

  time(&tt);
  sp = strrchr(argv[1],'/');
  if (sp) {
    sp++;
  } else {
    sp = argv[1];
  }
  strcpy(convtype, sp);

  if (strstr(convtype, "kaconvers")) server = "localhost:6809";
  if (strstr(convtype, "lconvers")) server = "localhost:6810";
  if (strstr(convtype, "suconvers")) server = "localhost:6811";
  if (strstr(convtype, "wconvers")) server = "localhost:3610";

  signal(SIGPIPE, SIG_IGN);

  if (tcgetattr(0, &prev_termios)) stop(convtype);
  curr_termios = prev_termios;
  echo = curr_termios.c_lflag & ECHO;
  curr_termios.c_lflag = 0;
  curr_termios.c_cc[VMIN] = 1;
  curr_termios.c_cc[VTIME] = 0;
  if (tcsetattr(0, TCSANOW, &curr_termios)) stop(convtype);

  while ((ch = getopt(argc, argv, "c:s:")) != EOF)
    switch (ch) {
    case 'c':
      channel = atoi(optarg);
      break;
    case 's':
      server = optarg;
      break;
    case '?':
      errflag = 1;
      break;
    }


  if (errflag || optind < argc) {
    fprintf(stderr, "usage: convers [-c channel] [-s host:service]\n");
    stop("");
  }
  if (!(addr = build_sockaddr(server, &addrlen))) {
    stop("build_sockaddr()");
  }

  close(3);
  if (socket(addr->sa_family, SOCK_STREAM, 0) != 3) stop("socket");
  if (connect(3, addr, addrlen)) stop("connect");

  for (; ; ) {
    do_select_call();
  }
}
