/*
 * Keyserv.c	A process that translates keypresses to
 *		ANSI or VT102 escape sequences.
 *		Communications with this process from minicom
 *		goes through pipes, file descriptors 3 & 4.
 *
 *		This file is part of the minicom communications package,
 *		Copyright 1991-1995 Miquel van Smoorenburg.
 *
 *		This program is free software; you can redistribute it and/or
 *		modify it under the terms of the GNU General Public License
 *		as published by the Free Software Foundation; either version
 *		2 of the License, or (at your option) any later version.
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
#include <config.h>
#include <unistd.h>
#include <stdlib.h>

#include "port.h"
#include "minicom.h"

/* Emulation modes */
#define EVT100	1
#define EANSI	2

/* Pipe file descriptors */
#define from_minicom 3
#define to_minicom   4

/* Set modes to normal */
int keypadmode = NORMAL;
int cursormode = NORMAL;

int mode = EVT100;		/* Emulation mode selector */
int parent;			/* Process ID of minicom */
jmp_buf mainloop;		/* To jump to after a 'HELLO' signal */
char **escseq;			/* Translation table to use */
static int argument;		/* Argument to 'HELLO' command */
static int esc_char = 1;	/* Escape character (initially ^A) */
static int bs_code = 8;		/* Code that backspace key sends */

char *st_vtesc[] = {
  "", "\033OP", "\033OQ", "\033OR", "\033OS", "", "", "", "", "", "",
  "\033[H", "", "\033[A", "\033[D", "\033[C", "\033[B", "\033[K", "",
  "", "\177" };

char *app_vtesc[] = {
  "", "\033OP", "\033OQ", "\033OR", "\033OS", "", "", "", "", "", "",
  "\033[H", "", "\033OA", "\033OD", "\033OC", "\033OB", "\033[K", "",
  "", "\177" };

char *ansiesc[] = {
  "", "\033OP", "\033OQ", "\033OR", "\033OS", "\033OT", "\033OU", "\033OV",
  "\033OW", "\033OX", "\033OY", "\033[H", "\033[V", "\033[A", "\033[D",
  "\033[C", "\033[B", "\033[Y", "\033[U", "0", "\177" };

/*
 * We got a signal. This means that there is some information for us.
 * Read it, and jump to the main loop.
 */
void handler(int dummy)
{
  unsigned char buf[8];
  int n;

  (void)dummy;
  signal(HELLO, handler);
  n = read(from_minicom, buf, 8);
  if (n <= 0) {
    n = 2;
    buf[0] = 0;
  }
  if (n % 2 == 1) {
    n++;
    read(from_minicom, buf + n, 1);
  }
  argument = buf[n-1];
  longjmp(mainloop, (int)buf[n - 2]);
}

/*
 * Send a string to the modem
 */
void sendstr(char *s)
{
  write(1, s, strlen(s));
}

/*
 * Main program of keyserv.
 */
int main(int argc, char **argv)
{
  int c;
  char ch;
  int f, fun;
  int stopped = 0;

  if (argc < 2 || (parent = atoi(argv[1])) == 0) {
    printf("Usage: %s <parent>\n", *argv);
    exit(1);
  }
  
  /* Initialize signal handlers */
  /* signal(SIGHUP, SIG_IGN); */
  signal(SIGQUIT, SIG_IGN);
  signal(SIGINT, SIG_IGN);
#ifdef SIGTSTP
  signal(SIGTSTP, SIG_IGN);
  signal(SIGTTIN, SIG_IGN);
  signal(SIGTTOU, SIG_IGN);
#endif
  signal(HELLO, handler);

  /* Set up escape sequence table */
  escseq = st_vtesc;

  /* Cbreak, no echo (minicom itself sets to raw if needed) */
  setcbreak(1);

  if ((fun = setjmp(mainloop)) != 0) {
    switch (fun) {

      /* We come here after minicom has told us something */

      case KVT100: /* VT100 keyboard */
        mode = EVT100;
        escseq = st_vtesc;
        break;
      case KANSI:  /* ANSI keyboard */
        mode = EANSI;
        escseq = ansiesc;
        break;
      case KKPST: /* Keypad in standard mode, not used */
        keypadmode = NORMAL; 
        break;
      case KKPAPP: /* Keypad in applications mode, not used */
        keypadmode = APPL;
        break;
      case KCURST: /* Standard cursor keys */
        cursormode = NORMAL;
        if (mode == EVT100)
          escseq = st_vtesc;
        break;
      case KCURAPP: /* cursor keys in applications mode */
        cursormode = APPL;
        if (mode == EVT100)
          escseq = app_vtesc;
        break;
      case KSTOP:  /* Sleep until further notice */
        stopped = 1;
        break;
      case KSIGIO: /* Wait for keypress and tell parent */
        kill(parent, ACK);
        f = read(0, &ch, 1);
        if (f == 1) {
          write(to_minicom, &ch, 1);
          kill(parent, HELLO);
        }
        break;
      case KSTART: /* Restart when stopped */
        stopped  = 0;
        break;
      case KSETBS: /* Set code that BS key sends */
        bs_code = argument;
        break;
      case KSETESC: /* Set escape character */
        esc_char = argument;
        break;
      default:
        break;
    }
    if (fun != KSIGIO)
      kill(parent, ACK);
  }
  /* Wait if stopped */
  if (stopped)
    pause();

  /* Main loop: read keyboard, send to modem */
  while (1) {
    c = wxgetch();
    if (c > 256 && c < 256 + NUM_KEYS) {
      sendstr(escseq[c - 256]);
    }
    if (c < 256) {
      if (c == K_ERA)
        c = bs_code;
      ch = c;
      /* Test for escape characters */
      if (c == esc_char || (esc_char == 128 && c > 128)) { 
        /* If we typed too fast, and the escape sequence
         * was not that of a function key, the next key
         * is already in the buffer.
         */
        if (c == esc_char && pendingkeys > 0) {
          ch = wxgetch();
        }
        write(to_minicom, &ch, 1);
        kill(parent, HELLO);
      } else {
        write(1, &ch, 1);
      }
    }
  }

  return 0;
}
