/*======================================================================*\
|*		Editor mined						*|
|*		keyboard input						*|
\*======================================================================*/

#include "mined.h"
#include "io.h"


#ifndef __TURBOC__
#include <sys/time.h>
#endif
#ifdef CURSES
#include <curses.h>
#include <strings.h>
#endif


voidfunc keyproc = I;	/* function addressed by entered function key */
static char fkeyshift;
#define max_ansi_params 10
int ansi_params;
int ansi_param [max_ansi_params];
char ansi_fini;
char keyshift = '0';	/* shift state of entered function key */

static
int keymap_delay = 0;		/* wait to detect mapped key sequence */
static
int default_keymap_delay = 900;	/* overridden by $MAPDELAY */

static
int escape_delay = 0;		/* wait to detect escape sequence */
static
int default_escape_delay = 450;	/* overridden by $ESCDELAY */

static
int dont_suppress_keymap = 1;

/* The flag queue_mode_mapped indicates when the byte input queue 
   has been stuffed with keyboard mapped characters, always being 
   encoded in UTF-8, while otherwise the queue contents are in 
   terminal native encoding (which may also be UTF-8, or CJK, or Latin-1).
 */
static
FLAG queue_mode_mapped = False;	/* queue contains mapped character(s) */

/* In hp_shift_mode key shift indications like ESC 2p or ESC 5h 
   are considered to determine the keyshift state.
 */
static
FLAG hp_shift_mode = False;

/* In sco_shift_mode key shift indications like ESC [2A or ESC [2M 
   are considered to determine the keyshift state.
 */
static
FLAG sco_shift_mode = False;


/*======================================================================*\
|*		Local function declaration				*|
\*======================================================================*/

static unsigned long _readchar _((void));


/*======================================================================*\
|*		Handling function key and keyboard mapping tables	*|
\*======================================================================*/

#ifndef msdos

#define in_status_line	(current_cursor_y == YMAX)

static
void
dummyfunc ()
{
}

extern struct fkeyentry fkeymap [];
extern struct fkeyentry fkeymap_xterm [];
extern struct fkeyentry fkeymap_rxvt [];
extern struct fkeyentry fkeymap_vt100 [];
extern struct fkeyentry fkeymap_hp [];
extern struct fkeyentry fkeymap_siemens [];
extern struct fkeyentry fkeymap_scoansi [];
extern struct fkeyentry fkeymap_interix [];

struct fkeyentry * fkeymap2 = fkeymap_xterm;

void
set_fkeymap (term)
  char * term;
{
  if (term == NIL_PTR) {
	return;
  } else if (* term == 'x') {
	fkeymap2 = fkeymap_xterm;
  } else if (* term == 'r') {
	fkeymap2 = fkeymap_rxvt;
  } else if (* term == 'h') {
	fkeymap2 = fkeymap_hp;
	hp_shift_mode = True;
  } else if (* term == 'v') {
	fkeymap2 = fkeymap_vt100;
  } else if (* term == 'i') {
	fkeymap2 = fkeymap_interix;
  } else if (* term == 's') {
	fkeymap2 = fkeymap_siemens;
	use_mouse = False;
  } else if (* term == 'o') {
	fkeymap2 = fkeymap_scoansi;
	sco_shift_mode = True;
	use_mouse = False;
  }
}

#else

void
set_fkeymap (term)
  char * term;
{
}

#endif


char * keyboard_mapping = "--";
static char * last_keyboard_mapping = "--";
static char * next_keyboard_mapping = "";

static keymaptabletype keyboard_map = (keymaptabletype) NIL_PTR;
static keymaptabletype last_keyboard_map = (keymaptabletype) NIL_PTR;

/**
   Set keymap.
 */
static
void
set_keymap (new_keymap, current, next)
  keymaptabletype new_keymap;
  char * current;
  char * next;
{
  last_keyboard_map = keyboard_map;
  last_keyboard_mapping = keyboard_mapping;
  keyboard_map = new_keymap;
  keyboard_mapping = current;
  next_keyboard_mapping = next;
}

/**
   Exchange current and previous keyboard mapping; set previous active.
   With HOP, reset keyboard mapping to none.
 */
void
toggleKEYMAP ()
{
  if (allow_keymap) {
	if (hop_flag > 0) {
		set_keymap ((keymaptabletype) NIL_PTR, "--", keymappingtable [0].shortcut);
	} else {
		set_keymap (last_keyboard_map, last_keyboard_mapping, last_keyboard_mapping);
	}
	flags_changed = True;
  } else {
	error ("Keyboard mapping not active");
  }
}

/**
   Set keyboard mapping.
 */
static
FLAG
selectKEYMAP (script)
  char * script;
{
  int i;

  if (script == NIL_PTR || * script == '\0') {
	set_keymap ((keymaptabletype) NIL_PTR, "--", keymappingtable [0].shortcut);
	flags_changed = True;
	return True;
  } else {
	/* map alternative shortcuts */
	if (strisprefix ("el", script)) {
		script = "gr";
	} else if (strisprefix ("ru", script)) {
		script = "cy";
	}
	/* map shortcut to keymap */
	for (i = 0; i < keymappingtable_len; i ++) {
	    if (strisprefix (keymappingtable [i].shortcut, script)) {
		set_keymap (keymappingtable [i].table,
			    keymappingtable [i].shortcut,
			    i + 1 == keymappingtable_len ?
			      "" : keymappingtable [i + 1].shortcut);
		flags_changed = True;
		return True;
	    }
	}
	return False;
  }
}

/**
   Set keyboard mapping.
 */
void
setKEYMAP (script)
  char * script;
{
  if (script != NIL_PTR && * script == '-') {
	/* set keymap as secondary map */
	script ++;
	selectKEYMAP (script);
	selectKEYMAP (NIL_PTR);
  } else if (script != NIL_PTR) {
	char * secondary = strchr (script, '-');
	if (secondary != NIL_PTR) {
		secondary ++;
		selectKEYMAP (secondary);
	}
	selectKEYMAP (script);
  }
}

#ifdef unused
/**
   Cycle through keyboard mappings.
 */
void
cycleKEYMAP ()
{
  selectKEYMAP (next_keyboard_mapping);
}
#endif

/**
   Enable keyboard mapping interactively.
   With HOP, cycle through keyboard mappings.
 */
void
setupKEYMAP ()
{
  if (allow_keymap) {
	if (hop_flag > 0) {
		hop_flag = 0;
		selectKEYMAP (next_keyboard_mapping);
	} else {
		handleKeymapmenu ();
	}
  } else {
	error ("Keyboard mapping not active");
  }
}

/**
   get_string_nokeymap prompts a string like get_string
   but doesn't apply international keyboard mapping
 */
int
get_string_nokeymap (prompt, inbuf, statfl, term_input)
  char * prompt;
  char * inbuf;
  FLAG statfl;
  char * term_input;
{
  int ret;

  dont_suppress_keymap = 0;
  ret = get_string (prompt, inbuf, statfl, term_input);
  dont_suppress_keymap = 1;

  return ret;
}


/*======================================================================*\
|*		Digits input						*|
\*======================================================================*/

/**
   get_digits () reads in a number.
   In contrast to get_number, it does no echoing, no messaging, reads 
   only decimal, and accepts zero length input.
   The last character typed in is returned.
   The resulting number is put into the integer the arguments points to.
   It is for use within I/O routines (e.g. reading mouse escape sequences 
   or cursor position report) and thus calls _readchar ().
 */
int
get_digits (result)
  int * result;
{
  register int index;
  register int count;

  index = read1byte ();
  if (index == quit_char) {
	quit = True;
  }
  * result = -1;
/* Convert input to a decimal number */
  count = 0;
  while (index >= '0' && index <= '9' && quit == False) {
	count *= 10;
	count += index - '0';
	* result = count;
	index = read1byte ();
	if (index == quit_char) {
		quit = True;
	}
  }

  if (quit) {
	return quit_char;
  }
  return index;
}


/*======================================================================*\
|*				Terminal input				*|
\*======================================================================*/

#define max_stamps 10
static long last_sec = 0;
static long last_msec = 0;
static int n_stamps = 0;
static int i_stamp = 0;
static long stamps [max_stamps];
static long total_deltas = 0;
long average_delta_readchar = 0;
long last_delta_readchar = 0;

static
void
timestamp_readchar ()
{
#ifndef __TURBOC__
  struct timeval now;
  long sec;
  long msec;

  gettimeofday (& now, 0);
  sec = now.tv_sec;
  msec = now.tv_usec / 1000;
  if (last_sec == 0) {
	last_delta_readchar = 1000;
  } else {
	last_delta_readchar = (sec - last_sec) * 1000 + msec - last_msec;
  }
  last_sec = sec;
  last_msec = msec;

  if (n_stamps < max_stamps) {
	n_stamps ++;
  } else {
	/* remove oldest stamp from statistics */
	total_deltas -= stamps [i_stamp];
  }
  stamps [i_stamp] = last_delta_readchar;
  /* add stamp to statistics */
  total_deltas += last_delta_readchar;
  i_stamp ++;
  if (i_stamp == max_stamps) {
	i_stamp = 0;
  }

  average_delta_readchar = total_deltas / n_stamps;
#endif
}

/*
 * Readchar () reads one character from the terminal.
 * There are problems due to interruption of the read operation by signals
 * (QUIT, WINCH). The waitingforinput flag is only a partial solution.
 * Unix doesn't provide sufficient facilities to handle these situations
 * neatly and properly. Moreover, different Unix versions yield different
 * surprising effects. However, the use of select () could still be
 * an improvement.
 */
static
unsigned long
readchar ()
{
  unsigned long c;

#ifdef msdos
#define winchhere
#endif

#ifdef winchhere
  FLAG waiting;

  if (winchg && waitingforinput == False) {
	/* In the Unix version, this is now done in __readchar () */
	RDwin ();
  }
  /* must save waitingforinput flag since in the MSDOS version, 
     readchar can be called recursively */
  waiting = waitingforinput;
#endif
  waitingforinput = True;

  c = _readchar ();
  while (command (c) == MOUSEescape
	&& mouse_button == releasebutton && report_release == False)
  {
	c = _readchar ();
  }
  report_release = False;

#ifdef winchhere
  waitingforinput = waiting;
#else
  waitingforinput = False;
#endif

  /* the modification
	if (quit) {
		c = quit_char;
	}
     (now in __readchar) must not be placed after resetting the flag
	waitingforinput = False;
     Otherwise a QUIT signal coming in just between these two would
     discard the last valid character just taken up.
  */

  timestamp_readchar ();

  return c;
}

/*
   readchar_nokeymap reads like readchar
   but doesn't apply international keyboard mapping
 */
static
unsigned long
readchar_nokeymap ()
{
  unsigned long c;
  int prev_dont_suppress_keymap = dont_suppress_keymap;

  dont_suppress_keymap = 0;
  c = readchar ();
  dont_suppress_keymap = prev_dont_suppress_keymap;

  return c;
}

/*
   readcharacter_allbuttons reads like readcharacter
   but always reports mouse button release
 */
unsigned long
readcharacter_allbuttons ()
{
  report_release = True;
  return readcharacter ();
}

#define dont_debug_mapped_keyboard

/*
   readcharacter_mapping () reads one character
   either with or without keyboard mapping
 */
static
unsigned long
readcharacter_mapping (map_keyboard, return_unicode)
  FLAG map_keyboard;
  FLAG return_unicode;
{
  int utfcount = 1;
  unsigned long unichar;
  int byte2;

  if (map_keyboard) {
	unichar = readchar ();
  } else {
	unichar = readchar_nokeymap ();
	/* double check needed here because command ('\n') == SNL */
	if (unichar == FUNcmd && command (unichar) == SNL) {
		return '\r';
	}
  }

  if (unichar == FUNcmd) {
	return FUNcmd;
  }

  if (utf8_input || queue_mode_mapped) {
	if ((unichar & 0x80) == 0x00) {
		utfcount = 1;
	} else if ((unichar & 0xE0) == 0xC0) {
		utfcount = 2;
		unichar = unichar & 0x1F;
	} else if ((unichar & 0xF0) == 0xE0) {
		utfcount = 3;
		unichar = unichar & 0x0F;
	} else if ((unichar & 0xF8) == 0xF0) {
		utfcount = 4;
		unichar = unichar & 0x07;
	} else if ((unichar & 0xFC) == 0xF8) {
		utfcount = 5;
		unichar = unichar & 0x03;
	} else if ((unichar & 0xFE) == 0xFC) {
		utfcount = 6;
		unichar = unichar & 0x01;
	} else /* illegal UTF-8 code */ {
		return unichar;
	}
	while (utfcount > 1) {
		unichar = (unichar << 6) | (readchar_nokeymap () & 0x3F);
		utfcount --;
	}
	if (! return_unicode && (cjk_text || mapped_text)) {
		return encodedchar (unichar);
	} else {
		return unichar;
	}
  } else if (cjk_term && multichar (unichar)) {
	if (text_encoding_tag == 'C' && unichar == 0x8E) {
		unichar = (unichar << 24)
			| (readchar_nokeymap () << 16)
			| (readchar_nokeymap () << 8)
			| readchar_nokeymap ();
	} else if (text_encoding_tag == 'J' && unichar == 0x8F) {
		unichar = (unichar << 16)
			| (readchar_nokeymap () << 8)
			| readchar_nokeymap ();
	} else {
		byte2 = readchar_nokeymap ();
		if (text_encoding_tag == 'G' && '0' <= byte2 && byte2 <= '9') {
			unichar = (unichar << 24)
				| (byte2 << 16)
				| (readchar_nokeymap () << 8)
				| readchar_nokeymap ();
		} else {
			unichar = (unichar << 8) | byte2;
		}
	}
	if (return_unicode || utf8_text) {
		unichar = lookup_encodedchar (unichar);
		if (no_unichar (unichar)) {
			return CHAR_INVALID;
		}
	}
	return unichar;
  } else {
	if (mapped_term) {
		if (! return_unicode && (cjk_text || mapped_text) && ! remap_chars ()) {
#ifdef debug_mapped_keyboard
	printf ("rc_m mapped (ret_u %d) -> %04X\n", return_unicode, unichar);
#endif
			return unichar;
		} else {
#ifdef debug_mapped_keyboard
	printf ("rc_m mapped (ret_u %d) -> %04X -> %04X ...\n", return_unicode, unichar, lookup_mappedtermchar (unichar));
#endif
			unichar = lookup_mappedtermchar (unichar);
			if (no_unichar (unichar)) {
#ifdef debug_mapped_keyboard
	printf ("rc_m mapped -> CHAR_INVALID\n");
#endif
				return CHAR_INVALID;
			}
		}
	}

	if (! return_unicode && (cjk_text || mapped_text)) {
#ifdef debug_mapped_keyboard
	printf ("rc_m -> %04X -> %04X\n", unichar, encodedchar (unichar));
#endif
		return encodedchar (unichar);
	} else {
#ifdef debug_mapped_keyboard
	printf ("rc_m -> %04X\n", unichar);
#endif
		return unichar;
	}
  }
}

/*
   readcharacter () reads one character
   keyboard mapping is suppressed
 */
unsigned long
readcharacter ()
{
  return readcharacter_mapping (False, False);
}

/*
   readcharacter_mapped () reads one character
   keyboard mapping is involved
 */
unsigned long
readcharacter_mapped ()
{
  return readcharacter_mapping (True, False);
}

/*
   readcharacter_unicode () reads one Unicode character
   keyboard mapping is suppressed
 */
unsigned long
readcharacter_unicode ()
{
  return readcharacter_mapping (False, True);
}

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

/*
 * DIRECT...getxy () reads in the information part of a cursor escape sequence
 */
static
void
DIRECTxtermgetxy (code)
  char code;	/* 'M' for normal mouse report, 't'/'T' for tracking */
{
  character button;
  int xpos;
  int ypos;
  char track [22];	/* for mouse hilite tracking */

  mouse_lastbutton = mouse_button;
  if (mouse_button == leftbutton || mouse_button == rightbutton || mouse_button == middlebutton) {
	mouse_prevbutton = mouse_button;
  }
  mouse_lastxpos = mouse_xpos;
  mouse_lastypos = mouse_ypos;

  if (code == 't') {
	mouse_button = releasebutton;
  } else if (code == 'T') {
	/* could be Siemens 97801/97808 page down ... !?! */
	button = _readchar_nokeymap ();
	button = _readchar_nokeymap ();
	button = _readchar_nokeymap ();
	button = _readchar_nokeymap ();
	mouse_button = releasebutton;
  } else {	/* code == 'M' */
	button = _readchar_nokeymap ();
	if (button == quit_char) {
		quit = True;
	}
	button -= 32;
	mouse_shift = button & 0x1C;
	if (button & 32) {
		mouse_button = movebutton;
	} else if (button & 64) {
		button &= 0x03;
		if (button == 0x00) {
			mouse_button = wheelup;
		} else {
			mouse_button = wheeldown;
		}
	} else {
		button &= 0x03;
		if (button == 0x00) {
			mouse_button = leftbutton;
		} else if (button == 0x01) {
			mouse_button = middlebutton;
		} else if (button == 0x02) {
			mouse_button = rightbutton;
		} else {
			mouse_button = releasebutton;
		}
	}
  }

  xpos = _readchar_nokeymap ();
  if (xpos == quit_char) {
	quit = True;
  }
  mouse_xpos = xpos - 33;
  ypos = _readchar_nokeymap ();
  if (ypos == quit_char) {
	quit = True;
  }
  mouse_ypos = ypos - 33 - MENU;

  if (mouse_hilite_tracking && mouse_button == leftbutton) {
	/* handle hilite tracking */
	if (mouse_ypos < 0 || mouse_ypos > last_y 
	    || (mouse_xpos == XMAX && disp_scrollbar) 
	    || is_menu_open ())
	{
		/* abort hilite tracking */
		putstring ("\033[0;0;0;0;0T");
		flush ();
	} else {
		build_string (track, "\033[1;%d;%d;%d;%dT", 
				mouse_xpos + 1, mouse_ypos + 1 + MENU, 
				1 + MENU, last_y + 3);
		putstring (track);
		flush ();
	}
  }
}

static
void
DIRECTcrttoolgetxy ()
{
  character c;
  int xpos;
  int ypos;

  mouse_lastbutton = mouse_button;
  if (mouse_button == leftbutton || mouse_button == rightbutton || mouse_button == middlebutton) {
	mouse_prevbutton = mouse_button;
  }
  mouse_lastxpos = mouse_xpos;
  mouse_lastypos = mouse_ypos;

  c = get_digits (& ypos); /* c should be ';' */
  c = get_digits (& xpos);
  if (c == 'l') {
	mouse_button = leftbutton;
  } else if (c == 'm') {
	mouse_button = middlebutton;
  } else if (c == 'r') {
	mouse_button = rightbutton;
  } else {
	mouse_button = releasebutton;
  }

  mouse_ypos = ypos - 1 - MENU;
  mouse_xpos = xpos - 1;
}

/*
 * DIRECT... () reads in a direct cursor movement input sequence.
 * Then it either performs a direct function (move/copy/paste) or 
 * invokes the menu functions.
 * Is no longer called as this is already handled in _readchar ().
 */
void
DIRECTxterm ()
{
  DIRECTxtermgetxy ('M');
  MOUSEescape ();
}
void
TRACKxterm ()
{
  DIRECTxtermgetxy ('t');
  MOUSEescape ();
}
void
TRACKxtermT ()
{
  DIRECTxtermgetxy ('T');
  MOUSEescape ();
}

void
DIRECTcrttool ()
{
  DIRECTcrttoolgetxy ();
  MOUSEescape ();
}


#ifdef CURSES
#ifdef KEY_MOUSE

#ifdef __PDCURSES__

/*
 * CURSgetxy () reads in the information of a mouse event
 */
static
void
CURSgetxy ()
{
# define mouse_state Mouse_status.changes
# define mouse_x MOUSE_X_POS
# define mouse_y MOUSE_Y_POS

  int button = 0;

  mouse_lastbutton = mouse_button;
  if (mouse_button == leftbutton || mouse_button == rightbutton || mouse_button == middlebutton) {
	mouse_prevbutton = mouse_button;
  }
  mouse_lastxpos = mouse_xpos;
  mouse_lastypos = mouse_ypos;

  request_mouse_pos ();

  if (BUTTON_CHANGED (1)) {
	button = 1;
  } else if (BUTTON_CHANGED (2)) {
	button = 2;
  } else if (BUTTON_CHANGED (3)) {
	button = 3;
  }

#ifdef BUTTON_SHIFT
  mouse_shift = (BUTTON_STATUS (button) & BUTTON_MODIFIER_MASK) == BUTTON_SHIFT;
#else
  mouse_shift = 0;
#endif

  if ((BUTTON_STATUS (button) & BUTTON_ACTION_MASK) == BUTTON_PRESSED) {
	if (button == 1) {
		mouse_button = leftbutton;
	} else if (button == 2) {
		mouse_button = middlebutton;
	} else if (button == 3) {
		mouse_button = rightbutton;
	} else {
		mouse_button = releasebutton;
	}
  } else {
	mouse_button = releasebutton;
  }

  mouse_xpos = mouse_x;
  mouse_ypos = mouse_y - MENU;
}

# else

static
void
CURSgetxy ()
{
#ifdef NCURSES_VERSION
  MEVENT mouse_info;
# define mouse_state mouse_info.bstate
# define mouse_x mouse_info.x
# define mouse_y mouse_info.y
#else
  unsigned long mouse_state;
# define mouse_x Mouse_status.x
# define mouse_y Mouse_status.y
#endif

  mouse_lastbutton = mouse_button;
  if (mouse_button == leftbutton || mouse_button == rightbutton || mouse_button == middlebutton) {
	mouse_prevbutton = mouse_button;
  }
  mouse_lastxpos = mouse_xpos;
  mouse_lastypos = mouse_ypos;

#ifdef NCURSES_VERSION
  getmouse (& mouse_info);
#else
  mouse_state = getmouse ();
#endif

#ifdef BUTTON_SHIFT
  mouse_shift = mouse_state & BUTTON_SHIFT;
#else
  mouse_shift = 0;
#endif

  if (mouse_state & BUTTON1_CLICKED) {
	mouse_button = leftbutton;
  } else if (mouse_state & BUTTON2_CLICKED) {
	mouse_button = middlebutton;
  } else if (mouse_state & BUTTON3_CLICKED) {
	mouse_button = rightbutton;
  } else {
	mouse_button = releasebutton;
  }

  mouse_xpos = mouse_x - 1;
  mouse_ypos = mouse_y - 1 - MENU;
}

#endif

#endif
#endif


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

#ifndef msdos

/* max. length of function key sequence to be detected
   (depending on the fkeymap table, approx. 7),
   or of keyboard mapping sequence plus potential mapping rest
*/
#define MAXCODELEN 33

/*
 * queue collects the keys of an Escape sequence typed in until the 
 * sequence can be detected or rejected.
 * If the queue is not empty, queue [0] contains the character next 
 * to be delivered by _readchar () (it's not a ring buffer).
 * The queue contents are always terminated by a '\0', so queue can also 
 * be taken as a character string.
 */
static character queue [MAXCODELEN + 1], * endp = queue;
static character ctrl_queue [MAXCODELEN + 1], * endcp = ctrl_queue;

#ifdef unused
static
int
q_empty ()
{
  return endp == queue ? 1 : 0;
}
#endif

static
int
q_notfull ()
{
  return endp - queue == MAXCODELEN ? 0 : 1;
}

static
void
q_clear ()
{
  endp = queue;
  endcp = ctrl_queue;
}

static
int
q_len ()
{
  return endp - queue;
}

static
void
q_put (c)
  character c;
/* queue must not be full prior to this call! */
{
  * endp = c;
  * ++ endp = '\0';
  if (c == '\0') {
	* endcp = '@';
  } else {
	* endcp = c;
  }
  * ++ endcp = '\0';
}

/*
static
character
q_unput ()
{
  character c;
  if (q_len () == 0) {
	return '\0';
  } else {
	endp --;
	endcp --;
	c = * endp;
	* endp = '\0';
	* endcp = '\0';
	return c;
  }
}
*/

static
character
q_get ()
{
  character c;
  register character * pd;
  register character * ps;

  c = * queue;
  pd = queue;
  ps = pd + 1;
  while (ps <= endp) {
	* pd ++ = * ps ++;
  }
  if (endp > queue) {
	endp --;
	endcp --;
  }

#define dont_debug_queue_delivery
#ifdef debug_queue_delivery
  printf ("%02X\n", c);
#endif

  return c;
}

/*
 * Look up key sequence in fkeymap table.
 * if matchmode == 0:
 * findkey (str) >=  0: escape sequence found
 *				(str == fkeymap [findkey (str)].fk)
 *			keyproc = fkeymap [i].fp (side effect)
 *		 == -1: escape sequence prefix recognised
 *				(str is prefix of some entry in fkeymap)
 *		 == -2: escape sequence not found
 *				(str is not contained in fkeymap)
 * * found == index of exact match if found
 * if matchmode == 1 (not used):
 * return -1 if any prefix detected (even if other exact match found)
 * if matchmode == 2 (not used):
 * don't return -1, only report exact or no match
 */
static
int
findkeyin (str, fkeymap, matchmode, found)
  char * str;
  struct fkeyentry * fkeymap;
  int matchmode;
  int * found;
{
  int lastmatch = 0;	/* last index with string matching prefix */
  register int i;
  int ret = -2;

  * found = -1;

  if (fkeymap [0].fk == NIL_PTR) {
	return ret;
  }

  i = lastmatch;
  do {
	char * strpoi = str;
	char * mappoi = fkeymap [i].fk;
	do {
		/* compare str with map entry */
		if (* strpoi != * mappoi) {
			break;
		}
		if (* strpoi) {
			strpoi ++;
			mappoi ++;
		}
	} while (* strpoi);

/*	if (strncmp (str, fkeymap [i].fk, strlen (str)) == 0) {*/
	if (* strpoi == '\0') {
		/* str is prefix of current entry */
		lastmatch = i;
/*		if (strlen (str) == strlen (fkeymap [i].fk)) {*/
		if (* mappoi == '\0') {
			/* str is equal to current entry */
			* found = i;
			keyproc = fkeymap [i].fp;
			fkeyshift = fkeymap [i].fkeyshift;
			if (matchmode == 1) {
				/* return index unless other prefix 
				   was or will be found */
				if (ret == -2) {
					ret = i;
				}
			} else {
				return i;
			}
		} else {
			/* str is partial prefix of current entry */
			if (matchmode == 1) {
				/* return -1 but continue to search 
				   for exact match */
				ret = -1;
			} else if (matchmode != 2) {
				return -1;
			} else {
			}
		}
	}
	++ i;
	if (fkeymap [i].fk == NIL_PTR) {
		i = 0;
		return ret;
	}
  } while (i != lastmatch);

  return ret;
}

static
int
findkey (str)
  char * str;
{
  int dummy;
  int res = findkeyin (str, fkeymap2, 0, & dummy);
  if (res == -2) {
	return findkeyin (str, fkeymap, 0, & dummy);
  } else {
	return res;
  }
}

/*
   Look up key sequence in keyboard mapping table.
   Similar to findkeyin but with modified parameter details.
 * if matchmode == 0 (not used):
 * mapkb (str) >=  0: key sequence str found
 *			* mapped set to mapped string
 *		 == -1: key sequence prefix recognised
 *				(str is prefix of some table entry)
 *		 == -2: key sequence not found
 *				(str is not contained in table)
 * * found == index of exact match if found
 * if matchmode == 1:
 * return -1 if any prefix detected (even if other exact match found)
 * if matchmode == 2:
 * don't return -1, only report exact or no match
 */
static
int
mapkb (str, kbmap, matchmode, found, mapped)
  char * str;
  keymaptabletype kbmap;
  int matchmode;
  char * * found;
  char * * mapped;
{
#ifdef keymapping_stringtables

  int lastmatch = 0;	/* last index with string matching prefix */
  register int i;
  int ret = -2;
  int len = strlen (str);

  * found = NIL_PTR;

  if (* kbmap == '\0') {
	return ret;
  }

  i = lastmatch;
  do {
	if (strncmp (str, kbmap, len) == 0) {
		/* str is prefix of current entry */
		lastmatch = i;
		if (len == strlen (kbmap)) {
			/* str is equal to current entry */
			* found = kbmap;
			* mapped = kbmap + len + 1;
			if (matchmode == 1) {
				/* return index unless other prefix 
				   was or will be found */
				if (ret == -2) {
					ret = i;
				}
			} else {
				return i;
			}
		} else {
			/* str is partial prefix of current entry */
			if (matchmode == 1) {
				/* return -1 but continue to search 
				   for exact match */
				ret = -1;
			} else if (matchmode != 2) {
				return -1;
			} else {
			}
		}
	}
	++ i;
	kbmap += strlen (kbmap) + 1;
	kbmap += strlen (kbmap) + 1;
	if (* kbmap == '\0') {
		i = 0;
		return ret;
	}
  } while (i != lastmatch);

  return ret;

#else

  int lastmatch = 0;	/* last index with string matching prefix */
  register int i;
  int ret = -2;

  * found = NIL_PTR;

  if (kbmap [0].fk == NIL_PTR) {
	return ret;
  }

  i = lastmatch;
  do {
	if (strncmp (str, kbmap [i].fk, strlen (str)) == 0) {
		/* str is prefix of current entry */
		lastmatch = i;
		if (strlen (str) == strlen (kbmap [i].fk)) {
			/* str is equal to current entry */
			* found = kbmap [i].fk;
			* mapped = kbmap [i].fp;
			if (matchmode == 1) {
				/* return index unless other prefix 
				   was or will be found */
				if (ret == -2) {
					ret = i;
				}
			} else {
				return i;
			}
		} else {
			/* str is partial prefix of current entry */
			if (matchmode == 1) {
				/* return -1 but continue to search 
				   for exact match */
				ret = -1;
			} else if (matchmode != 2) {
				return -1;
			} else {
			}
		}
	}
	++ i;
	if (kbmap [i].fk == NIL_PTR) {
		i = 0;
		return ret;
	}
  } while (i != lastmatch);

  return ret;

#endif
}

#endif	/* ifndef msdos */


#ifdef CURSES
chtype key;

extern int curs_readchar _((void));

static
void
showfkey ()
{
  build_string (text_buffer, "Curses function key entered: %03X / %04o", key, key);
  status_msg (text_buffer);
}


/*
 * Read a character, ignoring uninterpreted key strokes (SHIFT etc.)
 * also handles resize events
 */
int
curs_readchar ()
{
  unsigned long keycode;
  keycode = __readchar ();
  while (keycode >= KEY_MIN) {
	switch (keycode) {
#ifdef KEY_RESIZE
	case KEY_RESIZE:
  printf ("curses resize key received \n"); sleep (1);
			keyproc = showfkey;
			return FUNcmd;
#endif
#ifdef KEY_SHIFT_L
	case KEY_SHIFT_L:
	case KEY_SHIFT_R:
	case KEY_CONTROL_L:
	case KEY_CONTROL_R:
	case KEY_ALT_L:
	case KEY_ALT_R:
			break;
#endif
	default:	return keycode;
	}
	keycode = __readchar ();
  }

  return keycode;
}

/*
 * Mapping KEY_ code (curses) -> mined function
 */
struct {
	chtype keycode;
	voidfunc func;
	char fkeyshift;
} cursfunc [] =
{
	{KEY_F(1), F1},
	{KEY_F(2), F2},
	{KEY_F(3), F3},
	{KEY_F(4), F4},
	{KEY_F(5), F5},
	{KEY_F(6), F6},
	{KEY_F(7), F7},
	{KEY_F(8), F8},
	{KEY_F(9), F9},
	{KEY_F(10), F10},
	{KEY_F(11), F11},
	{KEY_F(12), F12},
	{KEY_IC, INSkey},
	{KEY_DC, DELkey},
	{KEY_HOME, HOMEkey},
	{KEY_END, ENDkey},
	{KEY_PPAGE, PU},
	{KEY_NPAGE, PD},
	{KEY_UP, MUP},
	{KEY_DOWN, MDN},
	{KEY_LEFT, MLF},
	{KEY_RIGHT, MRT},
#ifdef KEY_A2
	{KEY_A2, MUP},
#endif
#ifdef KEY_C2
	{KEY_C2, MDN},
#endif
#ifdef KEY_B1
	{KEY_B1, MLF},
#endif
#ifdef KEY_B3
	{KEY_B3, MRT},
#endif
	{KEY_B2, HOP},
	{KEY_A1, HOMEkey},
	{KEY_A3, PU},
	{KEY_C1, ENDkey},
	{KEY_C3, PD},
	{0x1BF, BLINE},
	{0x1C0, ELINE},

#ifdef PAD0
	{PAD0, PASTE},
#endif
#ifdef __PDCURSES__
	{PADSLASH, I},
	{PADSTAR, I},
	{PADMINUS, I},
	{PADPLUS, I},
	{PADSTOP, CUT},
	{PADENTER, SNL},
	{ALT_F, FILEMENU},
	{ALT_E, EDITMENU},
	{ALT_S, SEARCHMENU},
	{ALT_X, EXTRAMENU},
	{ALT_P, PARAMENU},
#endif

	{0}
};

/*
 * Look up mined function by curses key code
 * lookup_curskey (str) >=  0: keycode == cursfunc [lookup_curskey (str)].fk
 *			== -1: keycode is not contained in mapping table
 */
static
voidfunc
lookup_curskey (keycode)
  chtype keycode;
{
  register int i;

  i = 0;
  while (cursfunc [i].keycode != 0 && cursfunc [i].keycode != keycode) {
	i ++;
  }
  if (cursfunc [i].keycode == 0) {
	return showfkey;
  } else {
	keyshift |= cursfunc [i].fkeyshift;
	return cursfunc [i].func;
  }
}

#endif


/*
 * Functions to read a character from terminal.
   If _readchar () detects a function key sequence (according to the 
   table fkeymap), FUNcmd is returned, and the associated function is 
   stored in the variable keyproc.
   Keyboard mapping is applied.
 */

unsigned long
_readchar_nokeymap ()
{
  unsigned long c;
  int prev_dont_suppress_keymap = dont_suppress_keymap;

  dont_suppress_keymap = 0;
  c = _readchar ();
  dont_suppress_keymap = prev_dont_suppress_keymap;

  return c;
}

#ifndef msdos
static int choose_char _((char *));

static character rest_queue [MAXCODELEN + 1];
static FLAG have_rest_queue = False;

static
void
putback_rest (rest)
  char * rest;
{
  char old_rest_queue [MAXCODELEN + 1];

	if (! streq (rest, " ")) {
		if (have_rest_queue) {
			/**
			   overdraft characters to be mapped are 
			   put back into rest_queue 
			   to be considered for subsequent mapping
				-> Hiragana nihongo
				-> Hiragana nyi
				-> test abcde
			 */
			strcpy (old_rest_queue, (char *) rest_queue);
		} else {
			old_rest_queue [0] = '\0';
		}
		strcpy ((char *) rest_queue, rest);
		strcat ((char *) rest_queue, old_rest_queue);
		have_rest_queue = True;
	}
}

#endif

/* read 1 byte from queue; for curses, however, function 
   key indications must be passed on which are longer than 8 bits
 */
int
read1byte ()
{
#ifndef msdos
  character ch;
  character * cr;
  if (have_rest_queue) {
	ch = * rest_queue;
	cr = rest_queue;
	while (* (cr + 1) != '\0') {
		* cr = * (cr + 1);
		cr ++;
	}
	* cr = '\0';
	if (* rest_queue == '\0') {
		have_rest_queue = False;
	}
	return ch;
  } else
#endif
  {
#ifdef CURSES
	return curs_readchar ();
#else
	return __readchar ();
#endif
  }
}

#ifndef msdos

static
int
bytereadyafter (msec)
  int msec;
{
  return have_rest_queue || inputreadyafter (input_fd, msec);
}

/*
 * Is a character available within a specified number of milliseconds ?
 */
int
char_ready_within (msec)
  int msec;
{
  return (q_len () > 0) || bytereadyafter (msec);
}

#else	/* ifndef msdos */

int
char_ready_within (msec)
  int msec;
{
  return inputreadyafter (input_fd, msec);
}

#endif


#ifndef msdos
#define radical_stroke
#endif


#ifdef radical_stroke
static menuitemtype radicalsmenu [] =
{
	/* 1 stroke radicals */
	{"①-1:一 2:丨 3:丶 4:丿乀乁 5:乙乚乛 6:亅", dummyfunc, ""},
	/* 2 strokes radicals */
	{"②-7:二 8:亠 9:人亻 10:儿 11:入 12:八 13:冂 14:冖", dummyfunc, ""},
	{"②-15:冫 16:几 17:凵 18:刀刁刂 19:力 20:勹 21:匕", dummyfunc, ""},
	{"②-22:匚 23:匸 24:十 25:卜 26:卩 27:厂 28:厶 29:又", dummyfunc, ""},
	/* 3 strokes radicals */
	{"③-30:口 31:囗 32:土 33:士 34:夂 35:夊 36:夕 37:大夨 38:女 39:子孑孒孓", dummyfunc, ""},
	{"③-40:宀 41:寸 42:小 43:尢尣 44:尸 45:屮 46:山 47:巛巜川 48:工 49:己已巳", dummyfunc, ""},
	{"③-50:巾 51:干 52:乡幺 53:广 54:廴 55:廾 56:弋 57:弓 58:彐彑 59:彡 60:彳", dummyfunc, ""},
	/* 4 strokes radicals */
	{"④-61:心忄 62:戈 63:戶户戸 64:手扌才 65:支 66:攴攵 67:文 68:斗", dummyfunc, ""},
	{"④-69:斤 70:方 71:无 72:日 73:曰 74:月 75:木朩 76:欠 77:止 78:歹 79:殳", dummyfunc, ""},
	{"④-80:毋毌 81:比 82:毛 83:氏 84:气 85:水氵 86:火灬 87:爪爫 88:父 89:爻", dummyfunc, ""},
	{"④-90:丬爿 91:片 92:牙㸦 93:牛牜 94:犬犭", dummyfunc, ""},
	/* 5 strokes radicals */
	{"⑤-95:玄 96:玉王 97:瓜 98:瓦 99:甘 100:生 101:用甩 102:田由甲申甴电", dummyfunc, ""},
	{"⑤-103:疋 104:疒 105:癶 106:白 107:皮 108:皿 109:目", dummyfunc, ""},
	{"⑤-110:矛 111:矢 112:石 113:示礻 114:禸 115:禾 116:穴 117:立", dummyfunc, ""},
	/* 6 strokes radicals */
	{"⑥-118:竹 119:米 120:糸糹纟 121:缶 122:网罒罓䍏 123:羊 124:羽 125:老耂考", dummyfunc, ""},
	{"⑥-126:而 127:耒 128:耳 129:聿肀 130:肉 131:臣 132:自", dummyfunc, ""},
	{"⑥-133:至 134:臼 135:舌 136:舛 137:舟 138:艮 139:色 140:艸艹䒑", dummyfunc, ""},
	{"⑥-141:虍 142:虫 143:血 144:行 145:衣衤 146:襾西覀", dummyfunc, ""},
	/* 7 strokes radicals */
	{"⑦-147:見见 148:角 149:言訁讠 150:谷 151:豆 152:豕 153:豸", dummyfunc, ""},
	{"⑦-154:貝贝 155:赤 156:走赱 157:足 158:身 159:車车 160:辛", dummyfunc, ""},
	{"⑦-161:辰 162:辵辶 163:邑 164:酉 165:釆 166:里", dummyfunc, ""},
	/* 8 strokes radicals */
	{"⑧-167:金釒钅 168:長镸长 169:門门 170:阜阝", dummyfunc, ""},
	{"⑧-171:隶 172:隹 173:雨 174:靑青 175:非", dummyfunc, ""},
	/* 9 strokes radicals */
	{"⑨-176:面靣 177:革 178:韋韦 179:韭 180:音 181:頁页", dummyfunc, ""},
	{"⑨-182:風风 183:飛飞 184:食飠饣 185:首 186:香", dummyfunc, ""},
	/* 10 strokes radicals */
	{"⑩-187:馬马 188:骨 189:高髙 190:髟 191:鬥 192:鬯 193:鬲 194:鬼", dummyfunc, ""},
	/* 11 strokes radicals */
	{"⑪-195:魚鱼 196:鳥鸟 197:鹵 198:鹿 199:麥麦 200:麻", dummyfunc, ""},
	/* 12 strokes radicals */
	{"⑫-201:黃黄 202:黍 203:黑黒 204:黹", dummyfunc, ""},
	/* 13 strokes radicals */
	{"⑬-205:黽黾 206:鼎 207:鼓鼔 208:鼠鼡", dummyfunc, ""},
	/* 14 strokes radicals */
	{"⑭-209:鼻 210:齊齐", dummyfunc, ""},
	/* 15 strokes radicals */
	{"⑮-211:齒齿", dummyfunc, ""},
	/* 16 strokes radicals */
	{"⑯-212:龍龙 213:龜龟", dummyfunc, ""},
	/* 17 strokes radical */
	{"⑰-214:龠", dummyfunc, ""},
};
#endif


static
unsigned long
_readchar ()
{
  unsigned long ch;
#ifndef msdos
  int res;
  char * found;
  char * mapped;
  char * prev_found = NIL_PTR;
#endif
  char * choice;
  int choice_index;


  if (escape_delay == 0 || keymap_delay == 0) {
	char * env = getenv ("ESCDELAY");
	if (env) {
		(void) scan_int (env, & escape_delay);
	}
	if (escape_delay == 0) {
		escape_delay = default_escape_delay;
	}
	env = getenv ("MAPDELAY");
	if (env) {
		(void) scan_int (env, & keymap_delay);
	}
	if (keymap_delay == 0) {
		keymap_delay = default_keymap_delay;
	}
  }


#ifndef msdos
  if (q_len () > 0) {
	return q_get ();
  }
  queue_mode_mapped = False;
#endif

  keyshift = '0';


#ifdef radical_stroke
  /* plug-in specific input methods */
  if (allow_keymap && dont_suppress_keymap
	&& streq (keyboard_mapping, "rs")
  ) {
	int choice_col = x;
	int choice_line = y + 1;
	char radicalnumber [4];

	choice_index = -1;

	/* adjust menu position if too far down */
	if (choice_line + arrlen (radicalsmenu) + 2 > YMAX) {
		choice_line = y - arrlen (radicalsmenu) - 2;
		if (choice_line < 0) {
			choice_line = 0;
		}
	}

	/* adjust menu position if on status line */
	if (in_status_line) {
		choice_line = YMAX - arrlen (radicalsmenu) - 2;
		if (choice_line < 0) {
			choice_line = 0;
		}
		choice_col = lpos;
	}

	/* try to get radical, then character, from user */
	while (choice_index < 0) {
		int radical = popup_menu (radicalsmenu, arrlen (radicalsmenu), 
					choice_col, choice_line, 
					"Select radical", False, False, "1234567890x");
		if (radical < 0) {
			/* revert to previous mapping (or none) */
			toggleKEYMAP ();
			set_cursor_xy ();
			flush ();
			choice_index = 0;	/* continue */
		} else {
			int radical_row = radical / 11;
			int radical_col = radical % 11;
			mapped = radicalsmenu [radical_row].itemname;
			while (* mapped != '\0' &&
			       (* mapped < '0' || * mapped > '9'
			        || radical_col > 0))
			{
				if (* mapped == ':') {
					radical_col --;
				}
				mapped ++;
			}
			found = radicalnumber;
			while (* mapped >= '0' && * mapped <= '9') {
				* found ++ = * mapped ++;
			}
			* found = '\0';
			res = mapkb (radicalnumber, keyboard_map, 2, & found, & mapped);
			if (res >= 0) {
				/* first goto in my life and then 
				   such an exceptionally ugly one -
				   but it works well just this way */
				goto handle_mapped;
			} else {
				ring_bell ();
			}
		}
	}
  }
#endif


#ifdef CURSES

  key = read1byte ();
  if (key >= KEY_MIN) {
#ifdef KEY_MOUSE
	if (key == KEY_MOUSE) {
		CURSgetxy ();
		keyproc = MOUSEescape;
		return FUNcmd;
	}
#endif
	keyproc = lookup_curskey (key);
	return FUNcmd;
  }
  ch = key;

#else	/* ifdef CURSES */

#ifdef dosmouse
  {
	int ich = __readchar ();
	if (ich < 0) {
		DIRECTdosmousegetxy ();
		keyproc = MOUSEescape;
		return FUNcmd;
	} else {
		ch = ich;
	}
  }
#else
  ch = read1byte ();
#endif

#endif	/* else CURSES */


#ifdef msdos
  if (ch == '\000') {
# ifdef dosmouse
	ch = getch ();
# else
	ch = read1byte ();
# endif
	keyproc = pc_fkey_map [ch].fp;
	keyshift = pc_fkey_map [ch].fkeyshift;
# ifdef DEBUG
	if ((voidfunc) keyproc == (voidfunc) I) {
		return ch;
	}
	/* (voidfunc) is an identity cast here. It seems to be required 
	   for the sake of the apparently totally rotten microvax compiler */
# endif
	return FUNcmd;
  }
#endif


#ifndef msdos

    if (ch == '\033') {
	char second_esc_char = '\0';

	q_put (ch);

	while  ((res = findkey (ctrl_queue)) == -1 /* prefix of table entry */
		&& q_notfull ()
		&& bytereadyafter (escape_delay)
	       )
	{
		char prev_ch = ch;
		ch = read1byte ();

		/* accept ESC prefix as Alt modifier */
		if (detect_esc_alt && ch == '\033' && prev_ch == '\033') {
			if (bytereadyafter (escape_delay)) {
				ch = read1byte ();
				keyshift |= alt_mask;
			}
		}

		/* accept XTerm*modifyCursorKeys: 3 as well as 
		   device attributes reports (primary/secondary DA) */
		if (second_esc_char == '[' && prev_ch == '[' &&
			(ch == '>' || ch == '?')
		   ) {
			if (ch == '?') {
				keyshift |= report_mask;
			}
			ch = read1byte ();
		}

		/* handle generic shift state of ANSI sequences: */
		if (ch == ';' && second_esc_char == '[') {
			ch = read1byte ();
			if (ch >= '2' && ch <= '8') {
				/* ESC [1;2A -> ESC [1A + shift etc */
				keyshift |= ch - 1;
			} else if (ch == '1') {
				ch = read1byte ();
				if (ch >= '0' && ch <= '6') {
					/* xterm with alwaysUseMods: */
					/* ESC [1;11A -> ESC [1A + alt etc */
					keyshift |= applkeypad_mask | (ch + 1);
				} else {
					q_put (';');
					q_put ('1');
					q_put (ch);
				}
			} else {
				q_put (';');
				q_put (ch);
			}
			ch = read1byte ();
		} else if (ch >= '0' && ch <= '9'
			   && (second_esc_char == 'O' || hp_shift_mode)) {
			if (ch >= '2' && ch <= '8') {
				/* ESC O2A -> ESC OA + shift etc */
				/* HP: ESC 2p -> ESC p + shift etc */
				keyshift |= ch - 1;
			} else if (ch == '1') {
				ch = read1byte ();
				if (ch >= '0' && ch <= '6') {
					/* ESC O11P -> ESC OP + alt etc */
					/* HP: ESC 11p -> ESC p + alt etc */
					keyshift |= applkeypad_mask | (ch + 1);
				} else {
					q_put ('1');
					q_put (ch);
				}
			} else {
				q_put (ch);
			}
			ch = read1byte ();
		} else if (ch >= '0' && ch <= '9'
			   && second_esc_char == '[' && sco_shift_mode) {
			char newkeyshift;
			char newch = '\0';
			if (ch >= '2' && ch <= '8') {
				/* SCO: ESC [2M -> ESC [M + shift etc */
				newkeyshift = ch - 1;
				newch = read1byte ();
				if (newch >= 'A' && newch <= 'Z') {
					keyshift |= newkeyshift;
				} else {
					q_put (ch);
				}
			} else if (ch == '1') {
				ch = read1byte ();
				if (ch >= '0' && ch <= '6') {
					/* SCO: ESC [11M -> ESC [M + alt etc */
					newkeyshift = applkeypad_mask | (ch + 1);
					newch = read1byte ();
					if (newch >= 'A' && newch <= 'Z') {
						keyshift |= newkeyshift;
					} else {
						q_put ('1');
						q_put (ch);
					}
				} else {
					q_put ('1');
					q_put (ch);
					newch = read1byte ();
				}
			} else {
				q_put (ch);
				newch = read1byte ();
			}
			ch = newch;
		} else if (prev_ch >= '0' && prev_ch <= '9') {
			if (ch == '^') {
				/* ESC [5^ -> ESC [5~ + control (rxvt) */
				keyshift |= ctrl_mask;
				ch = '~';
			} else if (ch == '$') {
				/* ESC [5$ -> ESC [5~ + shift (rxvt) */
				keyshift |= shift_mask;
				ch = '~';
			} else if (ch == '@') {
				/* ESC [5@ -> ESC [5~ + control+shift (rxvt) */
				keyshift |= ctrlshift_mask;
				ch = '~';
			}
		} else if (ch >= 'a' && ch <= 'd') {
			if (prev_ch == 'O') {
				/* ESC Oa -> ESC OA + control (rxvt) */
				keyshift |= ctrl_mask;
				ch -= 'a' - 'A';
			} else if (prev_ch == '[') {
				/* ESC [a -> ESC [A + shift (rxvt) */
				keyshift |= shift_mask;
				ch -= 'a' - 'A';
			}
		}

		q_put (ch);
		if (second_esc_char == '\0') {
			second_esc_char = ch;
			if (second_esc_char == 'O') {
				keyshift |= applkeypad_mask;
			}
		}
	}

	if (quit) {
		keyproc = I;
		return FUNcmd;
	} else if (res < 0) { /* key pattern not detected in fkeymap table */
		if (second_esc_char == '[') {
			/* generic scanning for unknown ANSI sequence */
			(void) q_get (); /* swallow ESC */
			(void) q_get (); /* swallow [ */
			ansi_params = 0;
			do {
				int i;
				ansi_fini = get_digits (& i);
				if (ansi_params < max_ansi_params) {
					ansi_param [ansi_params] = i;
					ansi_params ++;
				}
			} while (ansi_fini == ';');

			keyproc = ANSIseq;
			return FUNcmd;
		} else if (keyshift & alt_mask) {
			/* restore the previously filtered ESCAPE */
			return '\033';
		} else {
			/* just deliver the typed characters */
			return q_get ();
		}
	} else {
		q_clear ();

		keyshift |= fkeyshift;

		if (keyproc == DIRECTxterm) {
			DIRECTxtermgetxy ('M');
			keyproc = MOUSEescape;
		} else if (keyproc == TRACKxterm) {
			DIRECTxtermgetxy ('t');
			keyproc = MOUSEescape;
		} else if (keyproc == TRACKxtermT) {
			DIRECTxtermgetxy ('T');
			keyproc = MOUSEescape;
		} else if (keyproc == DIRECTcrttool) {
			DIRECTcrttoolgetxy ();
			keyproc = MOUSEescape;
		}

		return FUNcmd;
	}
    }
#define dont_debug_kbmap
    else if (allow_keymap && dont_suppress_keymap
		&& keyboard_map != (keymaptabletype) NIL_PTR) {
	q_put (ch);
	while  ((res = mapkb (ctrl_queue, keyboard_map, 1, & found, & mapped)) == -1
		 /* prefix of some table entry */
		&& q_notfull ()
		&& bytereadyafter (keymap_delay)
	       )
	{
		q_put (read1byte ());
		/* exact match found? note for later consideration */
		if (found != NIL_PTR) {
			prev_found = found;
		}
	}
	/* possible results:
		res >= 0: match found, no further prefix match
		res == -1, prev_found != NIL_PTR: prefix with previous match
		res == -1: prefix only, no further key typed
		res == -2: no match with previous match
	*/

#ifdef debug_kbmap
	printf ("mapping %s, res %d, found %s -> ", ctrl_queue, res, found);
#endif
	if (res < 0 && prev_found != NIL_PTR) {
		res = 1;
		if (found == NIL_PTR) {
#ifdef debug_kbmap
	printf ("rest %s, ", ctrl_queue + strlen (prev_found));
#endif
			putback_rest (ctrl_queue + strlen (prev_found));
		}
	} else if (res == -1) {
		res = mapkb (ctrl_queue, keyboard_map, 2, & found, & mapped);
#ifdef debug_kbmap
	printf ("find again %s, found %s, ", ctrl_queue, found);
#endif
	}

#ifdef debug_kbmap
	printf ("res %d\n", res);
#endif
	if (res >= 0) { /* key pattern detected in key mapping table */
handle_mapped:
		q_clear ();

		/* check if mapping defines multiple choices */
		while (* mapped == ' ') {
			mapped ++;
		}
		choice = strchr (mapped, ' ');
		while (choice != NIL_PTR && * choice == ' ') {
			choice ++;
		}
		if (choice != NIL_PTR && * choice != '\0') {
			choice_index = choose_char (mapped);
#ifdef debug_kbmap
	printf (" choose_char (%s) -> %d\n", mapped, choice_index);
#endif
			if (choice_index < 0) {
				mapped = NIL_PTR;
				/* mapping cancelled */
			} else {
				while (choice_index > 0
					&& mapped != NIL_PTR
					&& * mapped != '\0')
				{
					mapped = strchr (mapped, ' ');
					if (mapped != NIL_PTR) {
						while (* mapped == ' ') {
							mapped ++;
						}
					}
					choice_index --;
				}
			}
		}

		if (mapped != NIL_PTR) {
			queue_mode_mapped = True;
			while (* mapped != '\0' && * mapped != ' ') {
				q_put (* mapped);
				mapped ++;
			}
		}
#ifdef debug_kbmap
	printf (" queued %s\n", ctrl_queue);
#endif
	}

	if (q_len () > 0) {
		return q_get () /* deliver the first mapped byte */;
	} else {
		ring_bell ();
		flush ();	/* clear key mapping menu */
		return _readchar ();
	}
    }

#endif	/* #ifndef msdos */

    else {
	return ch;
  }
}


#ifndef msdos

#define dont_debug_choose_char

static
int
choose_char (choices)
  char * choices;
{
  int choice_count = 0;
  char * choicepoi = choices;
  int choice_col = x;
  int choice_line = y + 1;

  char * thischoiceline;
  char * choicelines;
  menuitemtype * choicemenu;

  int li, ci, lastcol, lastrow;
  char * choicelinepoi;

  int selected;

#ifdef cut_picklist_to_screen
  FLAG reduced_menu = False;
#endif

	/* count choices */
	do {
		choice_count ++;
		choicepoi = strchr (choicepoi, ' ');
		while (choicepoi != NIL_PTR && * choicepoi == ' ') {
			choicepoi ++;
		}
	} while (choicepoi != NIL_PTR && * choicepoi != '\0');
#ifdef debug_choose_char
	printf ("choices <%s>\n", choices);
	printf ("choice_count %d\n", choice_count);
#endif

	lastrow = (choice_count - 1) / 10;
	choicelines = alloc (strlen (choices) + 1 + choice_count * 2);
	choicemenu = alloc ((lastrow + 1) * sizeof (menuitemtype));
	if (choicelines == NIL_PTR || choicemenu == (menuitemtype *) NIL_PTR) {
		if (choicelines != NIL_PTR) {
			free_space (choicelines);
		}
		ring_bell ();
		flush ();
		return -1;
	}

	/* adjust menu position if too far down */
	if (choice_line + lastrow + 3 > YMAX) {
		choice_line = y - lastrow - 3;
		if (choice_line < 0) {
			choice_line = 0;
		}
	}

	/* adjust menu position if on status line */
	if (in_status_line) {
		choice_line = YMAX - lastrow - 3;
		if (choice_line < 0) {
			choice_line = 0;
		}
		choice_col = lpos;
	}

	/* construct menu items */
	choicepoi = choices;
	while (* choicepoi == ' ') {
		choicepoi ++;
	}
	thischoiceline = choicelines;
	for (li = 0; li <= lastrow; li ++) {
		/* construct menu line */
		choicelinepoi = thischoiceline;
		if (li == lastrow) {
			lastcol = (choice_count - 1) % 10;
		} else {
			lastcol = 9;
		}
		for (ci = 0; ci <= lastcol; ci ++) {
			* choicelinepoi ++ = (ci + 1) % 10 + '0';
			* choicelinepoi ++ = ':';
			while (* choicepoi != ' ' && * choicepoi != '\0') {
				* choicelinepoi ++ = * choicepoi ++;
			}
			while (* choicepoi == ' ') {
				choicepoi ++;
			}
			if (ci < lastcol) {
				* choicelinepoi ++ = ' ';
			}
		}
		* choicelinepoi ++ = '\0';
		/* add menu line to menu */
#ifdef debug_choose_char
	printf ("choiceline <%s>\n", thischoiceline);
#endif
		choicemenu [li].itemname = thischoiceline;
		choicemenu [li].itemfu = dummyfunc;
		choicemenu [li].hopitemname = "";

		thischoiceline = choicelinepoi;
	}
#ifdef debug_choose_char
	printf ("choicelines alloc %d length %d\n",
		strlen (choices) + 1 + choice_count * 2 + lastrow + 1,
		thischoiceline - choicelines);
#endif

#ifdef cut_picklist_to_screen
	if (lastrow >= YMAX - 2) {
		lastrow = YMAX - 3;
		reduced_menu = True;
		if (! in_status_line) {
			status_msg ("Warning: full menu too large to display");
		}
	}
#endif
	selected = popup_menu (choicemenu, lastrow + 1, 
				choice_col, choice_line, 
				"Choose character", False, False, "1234567890");
	if (in_status_line) {
		redraw_prompt ();
#ifdef cut_picklist_to_screen
	} else if (reduced_menu) {
		clear_status ();
#endif
	}

#ifdef debug_choose_char
	printf ("selected %d\n", selected);
#endif

	free_space (choicemenu);
	free_space (choicelines);

	if (selected < choice_count) {
		return selected;
	} else {
		return -1;
	}
}

#endif


/*======================================================================*\
|*				End					*|
\*======================================================================*/
