text: /*
   * vt100.c	ANSI/VT102 emulator code.
   *		This code was integrated to the Minicom communications
   *		package, but has been reworked to allow usage as a separate
   *		module.
   *
   *		It's not a "real" vt102 emulator - it's more than that:
   *		somewhere between vt220 and vt420 in commands.
   *
   *		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.
   *
   * // jl 04.09.97 character map conversions in and out
   *    jl 06.07.98 use conversion tables with the capture file
   */
  #ifdef HAVE_CONFIG_H
  #include <config.h>
  #endif
  
  #include "rcsid.h"
  RCSID("$Id$")
  
  #include "port.h"
  #include "window.h"
  #include "vt100.h"
  #include "config.h"
  #include "configsym.h"
  
  #define OLD 0
  
  /*
   * The global variable esc_s holds the escape sequence status:
   * 0 - normal
   * 1 - ESC
   * 2 - ESC [
   * 3 - ESC [ ?
   * 4 - ESC (
   * 5 - ESC )
   * 6 - ESC #
   * 7 - ESC P
   */
  static int esc_s = 0;
  
  #define ESC 27
  
  /* Structure to hold escape sequences. */
  struct escseq {
    int code;
    char *vt100_st;
    char *vt100_app;
    char *ansi;
  };
  
  /* Escape sequences for different terminal types. */
  static struct escseq vt_keys[] = {
  #ifndef _DGUX_SOURCE
    { K_F1,	"OP",	"OP",	"OP" },
    { K_F2,	"OQ",	"OQ",	"OQ" },
    { K_F3,	"OR",	"OR",	"OR" },
    { K_F4,	"OS",	"OS",	"OS" },
    { K_F5,	"[16~",	"[16~",	"OT" },
    { K_F6,	"[17~",	"[17~",	"OU" },
    { K_F7,	"[18~",	"[18~",	"OV" },
    { K_F8,	"[19~",	"[19~",	"OW" },
    { K_F9,	"[20~",	"[20~",	"OX" },
    { K_F10,	"[21~",	"[21~",	"OY" },
    { K_F11,	"[23~",	"[23~",	"OY" },
    { K_F12,	"[24~",	"[24~",	"OY" },
    { K_HOME,	"[1~",	"[1~",	"[H" },
    { K_PGUP,	"[5~",	"[5~",	"[V" },
    { K_UP,	"[A",	"OA",	"[A" },
    { K_LT,	"[D",	"OD",	"[D" },
    { K_RT,	"[C",	"OC",	"[C" },
    { K_DN,	"[B",	"OB",	"[B" },
    { K_END,	"[4~",	"[4~",	"[Y" },
    { K_PGDN,	"[6~",	"[6~",	"[U" },
    { K_INS,	"[2~",	"[2~",	"[@" },
    { K_DEL,	"[3~",	"[3~",	"\177" },
    { 0,		NULL,	NULL,	NULL }
  #else
    { K_F1,      "[17~", "[17~", "OP" },
    { K_F2,      "[18~", "[18~", "OQ" },
    { K_F3,      "[19~", "[19~", "OR" },
    { K_F4,      "[20~", "[20~", "OS" },
    { K_F5,      "[21~", "[21~", "OT" },
    { K_F6,      "[23~", "[23~", "OU" },
    { K_F7,      "[24~", "[24~", "OV" },
    { K_F8,      "[25~", "[25~", "OW" },
    { K_F9,      "[26~", "[26~", "OX" },
    { K_F10,     "[28~", "[28~", "OY" },
    { K_F11,     "[29~", "[29~", "OZ" },
    { K_F12,     "[31~", "[31~", "OA" },
    { K_HOME,    "OP",   "OP",   "[H" },
    { K_PGUP,    "OQ",   "OQ",   "[V" },
    { K_UP,      "[A",   "OA",   "[A" },
    { K_LT,      "[D",   "OD",   "[D" },
    { K_RT,      "[C",   "OC",   "[C" },
    { K_DN,      "[B",   "OB",   "[B" },
    { K_END,     "OR",   "OR",   "[Y" },
    { K_PGDN,    "OS",   "OS",   "[U" },
    { K_INS,     "[1~",  "[1~",  "[@"  },
    { K_DEL,     "[3~",  "[3~",  "\177" },
    { 0,         NULL,   NULL,   NULL }
  #endif
  };
  
  /* Two tables for user-defined character map conversions.
   * defmap.h should contain all characters 0-255 in ascending order
   * to default to no conversion.    jl 04.09.1997
   */
  
  unsigned char vt_inmap[256] = {
  #include "defmap.h"
  };
  unsigned char vt_outmap[256] = {
  #include "defmap.h"
  };
  
  #if TRANSLATE
  /* Taken from the Linux kernel source: linux/drivers/char/console.c */
  static unsigned char * vt_map[] = {
  /* 8-bit Latin-1 mapped to the PC character set: '.' means non-printable */
  (unsigned char *)
  	"................"
  	"................"
  	" !\"#$%&'()*+,-./0123456789:;<=>?"
  	"@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
  	"`abcdefghijklmnopqrstuvwxyz{|}~."
  	"................"
  	"................"
  	"\377\255\233\234\376\235\174\025\376\376\246\256\252\055\376\376"
  	"\370\361\375\376\376\346\024\371\376\376\247\257\254\253\376\250"
  	"\376\376\376\376\216\217\222\200\376\220\376\376\376\376\376\376"
  	"\376\245\376\376\376\376\231\376\350\376\376\376\232\376\376\341"
  	"\205\240\203\376\204\206\221\207\212\202\210\211\215\241\214\213"
  	"\376\244\225\242\223\376\224\366\355\227\243\226\201\376\376\230",
  /* vt100 graphics */
  (unsigned char *)
  	"................"
  	"\0\0\0\0\0\0\0\0\0\0\376\0\0\0\0\0"
  	" !\"#$%&'()*+,-./0123456789:;<=>?"
  	"@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^ "
  	"\004\261\007\007\007\007\370\361\007\007\331\277\332\300\305\304"
  	"\304\304\137\137\303\264\301\302\263\363\362\343\330\234\007\0"
  	"................"
  	"................"
  	"\377\255\233\234\376\235\174\025\376\376\246\256\252\055\376\376"
  	"\370\361\375\376\376\346\024\371\376\376\247\257\254\253\376\250"
  	"\376\376\376\376\216\217\222\200\376\220\376\376\376\376\376\376"
  	"\376\245\376\376\376\376\231\376\376\376\376\376\232\376\376\341"
  	"\205\240\203\376\204\206\221\207\212\202\210\211\215\241\214\213"
  	"\376\244\225\242\223\376\224\366\376\227\243\226\201\376\376\230"
  };
  static char *vt_trans[2];
  static int vt_charset = 0;	/* Character set. */
  #endif
  
  #ifdef HAVE_SELECT
  static int vt_echo;		/* Local echo on/off. */
  #endif
  int vt_nl_delay;		/* Delay after CR key */
  static int vt_type   = ANSI;	/* Terminal type. */
  static int vt_wrap   = 0;	/* Line wrap on/off */
  static int vt_addlf  = 0;	/* Add linefeed on/off */
  static int vt_fg;		/* Standard foreground color. */
  static int vt_bg;		/* Standard background color. */
  static int vt_keypad;		/* Keypad mode. */
  static int vt_cursor;		/* cursor key mode. */
  static int vt_asis = 0;		/* 8bit clean mode. */
  static int vt_bs = 8;		/* Code that backspace key sends. */
  static int vt_insert = 0;	/* Insert mode */
  static int vt_crlf = 0;		/* Return sends CR/LF */
  static int vt_om;		/* Origin mode. */
  WIN *vt_win          = NIL_WIN;	/* Output window. */
  static int vt_docap;		/* Capture on/off. */
  static FILE *vt_capfp;		/* Capture file. */
  static void (*vt_keyb)();	/* Gets called for NORMAL/APPL switch. */
  static void (*termout)();	/* Gets called to output a string. */
  
  static int escparms[8];		/* Cumulated escape sequence. */
  #if OLD
  static int ptr = -2;		/* Index into escparms array. */
  #else
  static int ptr = 0;		/* Index into escparms array. */
  #endif
  static long vt_tabs[5];		/* Tab stops for max. 32*5 = 160 columns. */
  
  short newy1 = 0;		/* Current size of scrolling region. */
  short newy2 = 23;
  
  /* Saved color and posistions */
  static short savex = 0, savey = 0, saveattr = XA_NORMAL, savecol = 112;
  
  #if TRANSLATE
  static short savecharset;
  static char *savetrans[2];
  #endif
  
  /*
   * Initialize the emulator once.
   */
  void vt_install(fun1, fun2, win)
  void (*fun1)();
  void (*fun2)();
  WIN *win;
  {
    termout = fun1;
    vt_keyb = fun2;
    vt_win = win;
  }
  
  /* Partial init (after screen resize) */
  void vt_pinit(win, fg, bg)
  WIN *win;
  int fg, bg;
  {
    vt_win = win;
    newy1 = 0;
    newy2 = vt_win->ys - 1;
    wresetregion(vt_win);
    if (fg > 0) vt_fg = fg;
    if (bg > 0) vt_bg = bg;
    wsetfgcol(vt_win, vt_fg);
    wsetbgcol(vt_win, vt_bg);
  }
  
  /* Set characteristics of emulator. */
  void vt_init(type, fg, bg, wrap, add)
  int type;
  int fg;
  int bg;
  int wrap;
  int add;
  {
    vt_type = type;
    if (vt_type == ANSI) {
  	vt_fg = WHITE;
  	vt_bg = BLACK;
    } else {
  	vt_fg = fg;
  	vt_bg = bg;
    }
    if (wrap >= 0) vt_win->wrap = vt_wrap = wrap;
    vt_addlf = add;
    vt_insert = 0;
    vt_crlf = 0;
    vt_om = 0;
  
    newy1 = 0;
    newy2 = vt_win->ys - 1;
    wresetregion(vt_win);
    vt_keypad = NORMAL;
    vt_cursor = NORMAL;
  #ifdef HAVE_SELECT
    vt_echo = 0;
  #endif
    vt_tabs[0] = 0x01010100;
    vt_tabs[1] =
    vt_tabs[2] =
    vt_tabs[3] =
    vt_tabs[4] = 0x01010101;
  #if TRANSLATE
    vt_charset = 0;
    vt_trans[0] = savetrans[0] = vt_map[0];
    vt_trans[1] = savetrans[1] = vt_map[1];
  #endif
  #if OLD
    ptr = -2;
  #else
    ptr = 0;
    memset(escparms, 0, sizeof(escparms));
  #endif
    esc_s = 0;
  
    if (vt_keyb) (*vt_keyb)(vt_keypad, vt_cursor);
    wsetfgcol(vt_win, vt_fg);
    wsetbgcol(vt_win, vt_bg);
  }
  
  /* Change some things on the fly. */
  void vt_set(addlf, wrap, capfp, docap, bscode, echo, cursor, asis)
  int addlf;
  int wrap;
  FILE *capfp;
  int docap;
  int bscode;
  int echo;
  int cursor;
  int asis;
  {
    if (addlf >= 0) vt_addlf = addlf;
    if (wrap >= 0)  vt_win->wrap = vt_wrap = wrap;
    if (capfp != (FILE *)0) vt_capfp = capfp;
    if (docap >= 0) vt_docap = docap;
    if (bscode >= 0) vt_bs = bscode;
  #ifdef HAVE_SELECT
    if (echo >= 0) vt_echo = echo;
  #endif
    if (cursor >= 0) vt_cursor = cursor;
    if (asis >=0) vt_asis = asis;
  }
  
  /* Output a string to the modem. */
  static void v_termout(s, len)
  char *s;
  int len;
  {
  #ifdef HAVE_SELECT
    char *p;
  
    if (vt_echo) {
  	for(p = s; *p; p++) {
  		vt_out(*p);
  		if (!vt_addlf && *p == '\r') vt_out('\n');
  	}
  	wflush();
    }
  #endif
    (*termout)(s, len);
  }
  
  /*
   * Escape code handling.
   */
  
  /*
   * ESC was seen the last time. Process the next character.
   */
  static void state1(c)
  int c;
  {
    short x, y, f;
  
    switch(c) {
  	case '[': /* ESC [ */
  		esc_s = 2;
  		return;
  	case '(': /* ESC ( */
  		esc_s = 4;
  		return;
  	case ')': /* ESC ) */
  		esc_s = 5;
  		return;
  	case '#': /* ESC # */
  		esc_s = 6;
  		return;
  	case 'P': /* ESC P (DCS, Device Control String) */
  		esc_s = 7;
  		return;
  	case 'D': /* Cursor down */
  	case 'M': /* Cursor up */
  		x = vt_win->curx;
  		if (c == 'D')  { /* Down. */
  			y = vt_win->cury + 1;
  			if (y == newy2 + 1)
  				wscroll(vt_win, S_UP);
  			else if (vt_win->cury < vt_win->ys)
  				wlocate(vt_win, x, y);
  		}		
  		if (c == 'M')  { /* Up. */
  			y = vt_win->cury - 1;
  			if (y == newy1 - 1)
  				wscroll(vt_win, S_DOWN);
  			else if (y >= 0)
  				wlocate(vt_win, x, y);
  		}
  		break;
  	case 'E': /* CR + NL */
   		wputs(vt_win, "\r\n");
   		break;
   	case '7': /* Save attributes and cursor position */
   	case 's':
   		savex = vt_win->curx;
   		savey = vt_win->cury;
   		saveattr = vt_win->attr;
   		savecol = vt_win->color;
  #if TRANSLATE
  		savecharset = vt_charset;
  		savetrans[0] = vt_trans[0];
  		savetrans[1] = vt_trans[1];
  #endif
   		break;
   	case '8': /* Restore them */
   	case 'u':	
  #if TRANSLATE
  		vt_charset = savecharset;
  		vt_trans[0] = savetrans[0];
  		vt_trans[1] = savetrans[1];
  #endif
   		vt_win->color = savecol; /* HACK should use wsetfgcol etc */
   		wsetattr(vt_win, saveattr);
   		wlocate(vt_win, savex, savey);
   		break;
   	case '=': /* Keypad into applications mode */
  		vt_keypad = APPL;
  		if (vt_keyb) (*vt_keyb)(vt_keypad, vt_cursor);
   		break;
   	case '>': /* Keypad into numeric mode */
   		vt_keypad = NORMAL;
  		if (vt_keyb) (*vt_keyb)(vt_keypad, vt_cursor);
   		break;
   	case 'Z': /* Report terminal type */
  		if (vt_type == VT100)
  			v_termout("\033[?1;0c", 0);
   		else	
   			v_termout("\033[?c", 0);
   		break;	
   	case 'c': /* Reset to initial state */
   		f = XA_NORMAL;
   		wsetattr(vt_win, f);
  		vt_win->wrap = (vt_type != VT100);
  		if (vt_wrap != -1) vt_win->wrap = vt_wrap;
  		vt_crlf = vt_insert = 0;
  		vt_init(vt_type, vt_fg, vt_bg, vt_win->wrap, 0);
   		wlocate(vt_win, 0, 0);
   		break;
   	case 'H': /* Set tab in current position */
  		x = vt_win->curx;
  		if (x > 159) x = 159;
  		vt_tabs[x / 32] |= 1 << (x % 32);
  		break;
   	case 'N': /* G2 character set for next character only*/
   	case 'O': /* G3 "				"    */
   	case '<': /* Exit vt52 mode */
   	default:
   		/* ALL IGNORED */
   		break;
    }
    esc_s = 0;
    return;
  }
  
  /* ESC [ ... [hl] seen. */
  static void ansi_mode(on_off)
  int on_off;
  {
    int i;
  
    for(i = 0; i <= ptr; i++) {
  	switch(escparms[i]) {
  		case 4: /* Insert mode  */
  			vt_insert = on_off;
  			break;
  		case 20: /* Return key mode */
  			vt_crlf = on_off;
  			break;
  	}
    }
  }
  
  /*
   * ESC [ ... was seen the last time. Process next character.
   */
  static void state2(c)
  int c;
  {
    short x, y, attr, f;
    char temp[32];
  
    /* See if a number follows */
    if (c >= '0' && c <= '9') {
  #if OLD
  	if (ptr < 0) ptr = 0;
  #endif
  	escparms[ptr] = 10*(escparms[ptr]) + c - '0';
  	return;
    }
    /* Separation between numbers ? */
    if (c == ';') {
  #if OLD
  	if (ptr < 0) ptr = 0; /* keithr@primenet.com */
  	if (ptr >= 0 && ptr < 15) ptr++;
  #else
  	if (ptr < 15) ptr++;
  #endif
  	return;
    }
    /* ESC [ ? sequence */
  #if OLD
    if (ptr < 0 && c == '?') {
  #else
    if (escparms[0] == 0 && ptr == 0 && c == '?') {
  #endif
  	esc_s = 3;
  	return;
    }
  
    /* Process functions with zero, one, two or more arguments */
    switch(c) {
  	case 'A':
  	case 'B':
  	case 'C':
  	case 'D': /* Cursor motion */
  		if ((f = escparms[0]) == 0) f = 1;
  		x = vt_win->curx;
  		y = vt_win->cury;
  		x += f * ((c == 'C') - (c == 'D'));
  		if (x < 0) x = 0;
  		if (x >= vt_win->xs) x = vt_win->xs - 1;
  		if (c == 'B') { /* Down. */
  			y += f;
  			if (y >= vt_win->ys) y = vt_win->ys - 1;
  			if (y >= newy2 + 1) y = newy2;
  		}
  		if (c == 'A') { /* Up. */
  			y -= f;
  	 		if (y < 0) y = 0;
  			if (y <= newy1 - 1) y = newy1;
  		}	
  		wlocate(vt_win, x, y);
  		break;
  	case 'K': /* Line erasing */
  		switch(escparms[0]) {
  			case 0:
  				wclreol(vt_win);
  				break;
  			case 1:
  				wclrbol(vt_win);
  				break;
  			case 2:
  				wclrel(vt_win);
  				break;
  		}
  		break;
  	case 'J': /* Screen erasing */
  		x = vt_win->color;
  		y = vt_win->attr;
  		if (vt_type == ANSI) {
  			wsetattr(vt_win, XA_NORMAL);
  			wsetfgcol(vt_win, WHITE);
  			wsetbgcol(vt_win, BLACK);
  		}
  		switch(escparms[0]) {
  			case 0:
  				wclreos(vt_win);
  				break;
  			case 1:
  				wclrbos(vt_win);
  				break;
  			case 2:
  				winclr(vt_win);
  				break;
  		}
  		if (vt_type == ANSI) {
  			vt_win->color = x;
  			vt_win->attr = y;
  		}
  		break;
  	case 'n': /* Requests / Reports */
  		switch(escparms[0]) {
  			case 5: /* Status */
  				v_termout("\033[0n", 0);
  				break;
  			case 6:	/* Cursor Position */
  				sprintf(temp, "\033[%d;%dR", 
  					vt_win->cury + 1, vt_win->curx + 1);
  				v_termout(temp, 0);
  				break;
  		}
  		break;
  	case 'c': /* Identify Terminal Type */
  		if (vt_type == VT100) {
  			v_termout("\033[?1;2c", 0);
  			break;
  		}
  		v_termout("\033[?c", 0);
  		break;
  	case 'x': /* Request terminal parameters. */
  		  /* Always answers 19200-8N1 no options. */
  		sprintf(temp, "\033[%c;1;1;120;120;1;0x",
  			escparms[0] == 1 ? '3' : '2');
  		v_termout(temp, 0);
  		break;
   	case 's': /* Save attributes and cursor position */
   		savex = vt_win->curx;
   		savey = vt_win->cury;
   		saveattr = vt_win->attr;
   		savecol = vt_win->color;
  #if TRANSLATE
  		savecharset = vt_charset;
  		savetrans[0] = vt_trans[0];
  		savetrans[1] = vt_trans[1];
  #endif
   		break;
   	case 'u': /* Restore them */
  #if TRANSLATE
  		vt_charset = savecharset;
  		vt_trans[0] = savetrans[0];
  		vt_trans[1] = savetrans[1];
  #endif
   		break;
   		vt_win->color = savecol; /* HACK should use wsetfgcol etc */
   		wsetattr(vt_win, saveattr);
   		wlocate(vt_win, savex, savey);
   		break;
  	case 'h':
  		ansi_mode(1);
  		break;
  	case 'l':
  		ansi_mode(0);
  		break;
  	case 'H':
  	case 'f': /* Set cursor position */
  		if ((y = escparms[0]) == 0) y = 1;
  		if ((x = escparms[1]) == 0) x = 1;
  		if (vt_om) y += newy1;
  		wlocate(vt_win, x - 1, y - 1);
  		break;
  	case 'g': /* Clear tab stop(s) */
  		if (escparms[0] == 0) {
  			x = vt_win->curx;
  			if (x > 159) x = 159;
  			vt_tabs[x / 32] &= ~ (1 << x % 32);
  		}
  		if (escparms[0] == 3)
  			for(x = 0; x < 5; x++) vt_tabs[x] = 0;
  		break;
  	case 'm': /* Set attributes */
  #if OLD
  		  /* Without argument, esc-parms[0] is 0 */
  		if (ptr < 0) ptr = 0;  
  #endif
  		attr = wgetattr((vt_win));
  		for (f = 0; f <= ptr; f++) {
  		    if (escparms[f] >= 30 && escparms[f] <= 37)
  			wsetfgcol(vt_win, escparms[f] - 30);
  		    if (escparms[f] >= 40 && escparms[f] <= 47)
  			wsetbgcol(vt_win, escparms[f] - 40);
  		    switch(escparms[f]) {
  			case 0:
  				attr = XA_NORMAL;
  				wsetfgcol(vt_win, vt_fg);
  				wsetbgcol(vt_win, vt_bg);
  				break;
  			case 4:
  				attr |= XA_UNDERLINE;
  				break;
  			case 7:
  				attr |= XA_REVERSE;
  				break;
  			case 1:
  				attr |= XA_BOLD;
  				break;
  			case 5:
  				attr |= XA_BLINK;
  				break;
  			case 22: /* Bold off */
  				attr &= ~XA_BOLD;
  				break;
  			case 24: /* Not underlined */
  				attr &=~XA_UNDERLINE;
  				break;
  			case 25: /* Not blinking */
  				attr &= ~XA_BLINK;
  				break;
  			case 27: /* Not reverse */
  				attr &= ~XA_REVERSE;
  				break;
  			case 39: /* Default fg color */
  				wsetfgcol(vt_win, vt_fg);
  				break;
  			case 49: /* Default bg color */
  				wsetbgcol(vt_win, vt_bg);
  				break;
  				
  		    }
  		}
  		wsetattr(vt_win, attr);
  		break;
  	case 'L': /* Insert lines */
  		if ((x = escparms[0]) == 0) x = 1;
  		for(f = 0; f < x; f++)
  			winsline(vt_win);
  		break;	
  	case 'M': /* Delete lines */
  		if ((x = escparms[0]) == 0) x = 1;
  		for(f = 0; f < x; f++)
  			wdelline(vt_win);
  		break;	
  	case 'P': /* Delete Characters */
  		if ((x = escparms[0]) == 0) x = 1;
  		for(f = 0; f < x; f++)
  			wdelchar(vt_win);
  		break;	
  	case '@': /* Insert Characters */		
  		if ((x = escparms[0]) == 0) x = 1;
  		for(f = 0; f < x; f++)
  			winschar(vt_win);
  		break;	
  	case 'r': /* Set scroll region */
  		if ((newy1 = escparms[0]) == 0) newy1 = 1;
  		if ((newy2 = escparms[1]) == 0) newy2 = vt_win->ys;
  		newy1-- ; newy2--;
  		if (newy1 < 0) newy1 = 0;
  		if (newy2 < 0) newy2 = 0;
  		if (newy1 >= vt_win->ys) newy1 = vt_win->ys - 1;
  		if (newy2 >= vt_win->ys) newy2 = vt_win->ys - 1;
  		if (newy1 >= newy2) {
  			newy1 = 0;
  			newy2 = vt_win->ys - 1;
  		}
  		wsetregion(vt_win, newy1, newy2);
  		wlocate(vt_win, 0, newy1);
  		break;
  	case 'i': /* Printing */
  	case 'y': /* Self test modes */
  	default:
  		/* IGNORED */
  		break;
    }
    /* Ok, our escape sequence is all done */
    esc_s = 0;
  #if OLD
    ptr = -2;
  #else
    ptr = 0;
    memset(escparms, 0, sizeof(escparms));
  #endif
    return;		
  }
    
  /* ESC [? ... [hl] seen. */
  static void dec_mode(on_off)
  int on_off;
  {
    int i;
  
    for(i = 0; i <= ptr; i++) {
  	switch(escparms[i]) {
  		case 1: /* Cursor keys in cursor/appl mode */
  			vt_cursor = on_off ? APPL : NORMAL;
  			if (vt_keyb) (*vt_keyb)(vt_keypad, vt_cursor);
  			break;
  		case 6: /* Origin mode. */
  			vt_om = on_off;
  			wlocate(vt_win, 0, newy1);
  			break;
  		case 7: /* Auto wrap */
  			vt_win->wrap = on_off;
  			break;
  		case 25: /* Cursor on/off */
  			wcursor(vt_win, on_off ? CNORMAL : CNONE);
  			break;
  		case 67: /* Backspace key sends. (FIXME: vt420) */
  			/* setbackspace(on_off ? 8 : 127); */
  			break;
  		default: /* Mostly set up functions */
  			/* IGNORED */
  			break;
  	}
    }
  }
  
  /*
   * ESC [ ? ... seen.
   */
  static void state3(c)
  int c;
  {
    /* See if a number follows */
    if (c >= '0' && c <= '9') {
  #if OLD
  	if (ptr < 0) ptr = 0;
  #endif
  	escparms[ptr] = 10*(escparms[ptr]) + c - '0';
  	return;
    }
  #if OLD
    /* ESC [ ? number seen */
    if (ptr < 0) {
  	esc_s = 0;
  	return;
    }
  #endif
    switch(c) {
  	case 'h':
  		dec_mode(1);
  		break;
  	case 'l':
  		dec_mode(0);
  		break;
  	case 'i': /* Printing */
  	case 'n': /* Request printer status */
  	default:
  		/* IGNORED */
  		break;
    }
    esc_s = 0;
  #if OLD
    ptr = -2;
  #else
    ptr = 0;
    memset(escparms, 0, sizeof(escparms));
  #endif
    return;
  }
  
  /*
   * ESC ( Seen.
   */
  /*ARGSUSED*/
  static void state4(c)
  int c;
  {
    /* Switch Character Sets. */
  #if !TRANSLATE
    /* IGNORED */
    (void)c;
  #else
    switch(c) {
  	case 'A':
  	case 'B':
  		vt_trans[0] = vt_map[0];
  		break;
  	case '0':
  	case 'O':
  		vt_trans[0] = vt_map[1];
  		break;
    }
  #endif
    esc_s = 0;
  }
  
  /*
   * ESC ) Seen.
   */
  /*ARGSUSED*/
  static void state5(c)
  int c;
  {
    /* Switch Character Sets. */
  #if !TRANSLATE
    /* IGNORED */
    (void)c;
  #else
    switch(c) {
  	case 'A':
  	case 'B':
  		vt_trans[1] = vt_map[0];
  		break;
  	case 'O':
  	case '0':
  		vt_trans[1] = vt_map[1];
  		break;
    }
  #endif
    esc_s = 0;
  }
  
  /*
   * ESC # Seen.
   */
  /*ARGSUSED*/
  static void state6(c)
  int c;
  {
    int x, y;
  
    /* Double height, double width and selftests. */
    switch(c) {
  	case '8':
  		/* Selftest: fill screen with E's */
  		vt_win->doscroll = 0;
  		vt_win->direct = 0;
  		wlocate(vt_win, 0, 0);
  		for(y = 0; y < vt_win->ys; y++) {
  			wlocate(vt_win, 0, y);
  			for(x = 0; x < vt_win->xs; x++)
  				wputc(vt_win, 'E');
  		}
  		wlocate(vt_win, 0, 0);
  		vt_win->doscroll = 1;
  		wredraw(vt_win, 1);
  		break;
  	default:
  		/* IGNORED */
  		break;
    }
    esc_s = 0;
  }
  
  /*
   * ESC P Seen.
   */
  static void state7(c)
  int c;
  {
    /*
     * Device dependant control strings. The Minix virtual console package
     * uses these sequences. We can only turn cursor on or off, because
     * that's the only one supported in termcap. The rest is ignored.
     */
    static char buf[17];
    static int pos = 0;
    static int state = 0;
  
    if (c == ESC) {
    	state = 1;
    	return;
    }
    if (state == 1) {
    	buf[pos] = 0;
    	pos = 0;
    	state = 0;
    	esc_s = 0;
    	if (c != '\\') return;
    	/* Process string here! */
    	if (!strcmp(buf, "cursor.on")) wcursor(vt_win, CNORMAL);
    	if (!strcmp(buf, "cursor.off")) wcursor(vt_win, CNONE);
    	if (!strcmp(buf, "linewrap.on")) {
    		vt_wrap = -1;
    		vt_win->wrap = 1;
    	}
    	if (!strcmp(buf, "linewrap.off")) {
    		vt_wrap = -1;
    		vt_win->wrap = 0;
    	}
    	return;
    }
    if (pos > 15) return;
    buf[pos++] = c;
  }
  
  void vt_out(ch)
  int ch;
  {
    int f;
    unsigned char c;
    int go_on = 0;
  
    if (!ch) return;
  
  #if OLD
    if (ptr == -2) { /* Initialize */
  	ptr = -1;
  	for(f = 0; f < 8; f++) escparms[f] = 0;
    }
  #endif
  
    c = (unsigned char)ch;
    
    if (vt_docap == 2) /* Literal. */
  	fputc(c, vt_capfp);
  
    /* Process <31 chars first, even in an escape sequence. */
    switch(c) {
  	case 5: /* AnswerBack for vt100's */
  		if (vt_type != VT100) {
  			go_on = 1;
  			break;
  		}
  		v_termout("Minicom" VERSION, 0);
  		break;
  	case '\r': /* Carriage return */
  		wputc(vt_win, c);
  		if (vt_addlf) {
  			wputc(vt_win, '\n');
  			if (vt_docap == 1)
  				fputc('\n', vt_capfp);
  		}
  		break;
  	case '\t': /* Non - destructive TAB */
  		/* Find next tab stop. */
  		for(f = vt_win->curx + 1; f < 160; f++)
  			if (vt_tabs[f / 32] & (1 << f % 32)) break;
  		if (f >= vt_win->xs) f = vt_win->xs - 1;
  		wlocate(vt_win, f, vt_win->cury);
  		if (vt_docap == 1) fputc(c, vt_capfp);
  		break;
  	case 013: /* Old Minix: CTRL-K = up */
  		wlocate(vt_win, vt_win->curx, vt_win->cury - 1);
  		break;
  	case '\f': /* Form feed: clear screen. */
  		winclr(vt_win);
  		wlocate(vt_win, 0, 0);
  		break;
  #if !TRANSLATE
  	case 14:
  	case 15:  /* Change character set. Not supported. */
  		break;
  #else
  	case 14:
  		vt_charset = 1;
  		break;
  	case 15:
  		vt_charset = 0;
  		break;
  #endif
  	case 24:
  	case 26:  /* Cancel escape sequence. */
  		esc_s = 0;
  		break;
  	case ESC: /* Begin escape sequence */
  		esc_s = 1;
  		break;
  	case 128+ESC: /* Begin ESC [ sequence. */
  		esc_s = 2;
  		break;
  	case '\n':
  	case '\b':
  	case 7: /* Bell */
  		wputc(vt_win, c);
  		if (vt_docap == 1)
  			fputc(c, vt_capfp);
  		break;
  	default:
  		go_on = 1;
  		break;
    }
    if (!go_on) return;
  
    /* Now see which state we are in. */
    switch(esc_s) {
  	case 0: /* Normal character */
  		if (vt_docap == 1)
  			fputc(P_CONVCAP[0]=='Y'? vt_inmap[c]: c, vt_capfp);
  	        c = vt_inmap[c];    /* conversion 04.09.97 / jl */
  #if TRANSLATE
  		if (vt_type == VT100 && vt_asis == 0) {
  			if (vt_insert)
  				winschar2(vt_win, vt_trans[vt_charset][c], 1);
  			else
  				wputc(vt_win, vt_trans[vt_charset][c]);
  		} else {
  #endif
  		if (vt_insert)
  			winschar2(vt_win, c, 1);
  		else
  			wputc(vt_win, c);
  #if TRANSLATE
  		}
  #endif
  		break;
  	case 1: /* ESC seen */
  		state1(c);
  		break;
  	case 2: /* ESC [ ... seen */
  		state2(c);
  		break;
  	case 3:
  		state3(c);
  		break;
  	case 4:
  		state4(c);
  		break;
  	case 5:
  		state5(c);
  		break;
  	case 6:
  		state6(c);
  		break;
  	case 7:
  		state7(c);
  		break;	
    }
  }
  
  /* Translate keycode to escape sequence. */
  void vt_send(c)
  int c;
  {
    char s[3];
    int f;
    int len = 1;
  
    /* Special key? */
    if (c < 256) {
  	/* Translate backspace key? */
  	if (c == K_ERA) c = vt_bs;
  	s[0] = vt_outmap[(unsigned char) c];  /* conversion 04.09.97 / jl */
  	s[1] = 0;
  	/* CR/LF mode? */
  	if (c == '\r' && vt_crlf) {
  		s[1] = '\n';
  		s[2] = 0;
  		len = 2;
  	}
  	v_termout(s, len);
  	if (vt_nl_delay > 0 && c == '\r')
  		usleep(1000 * vt_nl_delay);
  	return;
    }
  
    /* Look up code in translation table. */
    for(f = 0; vt_keys[f].code; f++)
  	if (vt_keys[f].code == c) break;
    if (vt_keys[f].code == 0) return;
  
    /* Now send appropriate escape code. */
    v_termout("\033", 0);
    if (vt_type == VT100) {
  	if (vt_cursor == NORMAL)
  		v_termout(vt_keys[f].vt100_st, 0);
  	else
  		v_termout(vt_keys[f].vt100_app, 0);
    } else
  	v_termout(vt_keys[f].ansi, 0);
  }
  