/*+++*
 *  title:	fkeys.c
 *  abstract:	almost terminal-independent function key support for JOVE
 *  author:	T.R.Hageman, AZG, Groningen
 *  created:	august 1988
 *  modified:	3-Oct-88 [TRH] add delayed prompt support
 *		1-Dec-88, rename getkey() to getchar()
 *		2-Apr-89, complete revision of recognizer algorithm
 *			tuned to fast recognition of first character.
 *			Replaced delayed display kludge with timeout.
 *		1-Jul-89, fix bug with 8-bit characters which prevented
 *			them to be stored in macros.
 *		28-Jul-89, remove K0,K6-K9 (not supported by terminfo)
 *			add kb(backspace), kB(backtab) k;(f10)
 *			PU,PD,HM,EN(XENIX keynames)
 *		12-Jan-90, some cleanups; improve DEBUG mode.
 *		5-Feb-90, add "extended" special keys (AIX);
 *			add `keycode remap' for some keys;
 *			match states instead of characters now.
 *		28-Feb-90, put error messages in buffer.
 *		16-Dec-90, add kY(Undo).
 *		10-Jan-91, implement KEYALIAS for weird capability names.
 *		 9-Feb-91, fix bug in KEYALIAS
 *		15-Feb-91, "use-function-keys" now defaults to "on";
 *			new variable "keypad" controls appl/numeric keypad.
 *		25-Mar-92, revise KEYALIAS; systems to support aliases for
 *			can now be specified as a compilation option.
 *		13-Apr-92, update KEYALIAS with new termcap keydefs;
 *			write DEBUG info to JOVE buffer.
 *  description:
 *	FKeyInit() builds a recognizer for all function keys that can be
 *	defined in the termcap entry. The available keys are mapped to
 *	characters within the range FK_BASE..FK_BASE+NFKEYS.
 *	The keymap command tables are extended to support this range.
 *	The actual code assigned to a key is not fixed, but rather depends
 *	on the available key definitions.
 *	However this is transparent for the user since the function keys
 *	can be assigned to by symbolic names of the form "^:xx",
 *	where "xx" is the 'termcap' key name (see extend.c(addgetc) and fmt.c).
 *
 *	getchar() supersedes the original which is now named rawchar()
 *	(see tty.c). It parses the "raw" input sequence to intercept
 *	the function keys. rawchar() expects a parameter 'time_out'
 *	which indicates whether it should time out.
 *
 *	Finally the boolean variable "use-function-keys" (UseFkeys)
 *	is available to enable/disable function key recognition.
 *	You may want to disable it if you have a terminal that 'eats'
 *	some standard emacs functions (like tviXXX arrow keys that send
 *	 ^H ^J ^K ^L)
 *---*/

#include "tune.h"	/* to allow definition of debug flags there. */

#ifdef DEBUG		/* General debug flag, turn on file-specific flag. */
#   ifndef FKEYS_DEBUG
#	define FKEYS_DEBUG	1
#   endif
#else
#   if FKEYS_DEBUG	/* File-specific debug flag, turn on general flag. */
#	define DEBUG
#   endif
#endif

#include "jove.h"

#ifdef FUNCKEYS		/* the whole file */

RCS("$Id: fkeys.c,v 14.32.0.12 1994/06/24 17:55:03 tom Exp tom $")


DEF_INT( "keypad", UseKeyPad, V_BOOL|V_TTY_RESET ) = YES; _IF(def FUNCKEYS)

#ifdef TERMCAP		/* the whole file */

DEF_INT( "use-function-keys", UseFKeys, V_BOOL ) = YES; _IF(def FUNCKEYS)_IF(def TERMCAP)_IF(def PRIVATE)

#include "termcap.h"

#if FKEYS_DEBUG
#   include "io.h"
#   define DPRINTF(x)	ins_str(sprint x, 0)
#   define KEYID(i)	(((char *)&FKeyMap[i])[0]&0177), (((char *)&FKeyMap[i])[1]&0177)
#   define STATE(p)	(int)((p) - FKeyState)
#else
#   define DPRINTF(x)
#endif

/* Where to start numbering function keys. */
#ifndef FK_BASE
#   define FK_BASE	0200
#endif

/*
 * KEYALIAS
 * stuff to map system-dependent obscure termcap keynames to JOVEs standard.
 */
#define KEY_SCO	(1<<0)
#define KEY_AIX	(1<<1)
#define KEY_EXT	(1<<2)
#define KEY_SUN	KEY_EXT	/* obsolete name */

#ifndef KEYALIAS
    /* provide some defaults */
#   if (sun || __sun__)
#	define KEYALIAS	KEY_SUN
#   endif
#   if (aiws || __aiws__ || aix || __aix__)
#	define KEYALIAS	KEY_AIX
#   endif
#   ifdef SCO_SYSV
#	define KEYALIAS	KEY_SCO
#   endif
#   ifndef KEYALIAS
#	ifndef TINY
#	    define KEYALIAS	KEY_EXT
#	else
#	    define KEYALIAS	0
#	endif
#   endif
#else
#   if (2 * KEYALIAS -1 == 2 * -1)	/* defined, but empty */
#	define KEYALIAS KEY_EXT	/* to generate a warning */
#   endif
#endif

#if KEYALIAS
#   define ALIAS	'\200'
#   define AliasTo	ALIAS |
#   define Aliased(cp)	(((char *)(cp))[0] & ALIAS)
#   define UnAlias(cp)	(((char *)(cp))[0] &= ~ALIAS)
#
#   define SHIFT	ALIAS
#   define shifted	| SHIFT
#   define Shifted(cp)	(((char *)(cp))[1] & SHIFT)
#   define UnShift(cp)	(((char *)(cp))[1] &= ~SHIFT)
#
#   define FK_SHIFT	01000
#endif

#ifndef SIZEOF_SHORT	/* assume we can get away with dirty short * casts */
#   define SIZEOF_SHORT	2
#endif

#define KEYLEN	5		/* max. average key sequence length */

#if (MAXSTATE > 256 || MAXSTATE < -256)	/* no checks if negative */
#   define Index	short
#else
#   define Index	char
#   if !MAXSTATE	/* includes !defined(MAXSTATE) */
#	if (!__CHAR_UNSIGNED__) && (KEYALIAS <= KEY_SCO)
#	    define MAXSTATE	-128
#	else
#	    define MAXSTATE	-256
#	endif
#   endif
#endif
#if (!__CHAR_UNSIGNED__) && (128 < MAXSTATE && MAXSTATE <= 256) || (-256 <= MAXSTATE && MAXSTATE < -128)
#   define UI		_UC_
#else
#   define UI
#endif

typedef struct {
	Index	fk_prev;	/* previous state to be matched */
	Index	fk_alt;		/* next state taken if no match */
	short	fk_final;	/* character to translate to	*/
} FKEY;

/*
 * The next table is indexed by the raw input character to retrieve the
 * index of the head of its state list. Initial states are marked with
 * a NIL fk_prevc field and are always at the head of the list.
 * Index NIL means no state is available.
 * [ for reasons of space efficiency this vector is overlaid with the
 *   keyname scan string, which isn't needed after the initialisation ]
 * The scan string contains a list of entries "kx" or "kx=c",
 * where the latter form requests a `keycode remap' to code `c'.
#if KEYALIAS
 * or "kx" AliasTo "ky", which internally renames real capability `kx' to `ky'.
#   if KEYSHIFT
 * or "ka" AliasTo "kb" shifted, which also maps key to `MetaKey key'.
 * a `shifted' key should be defined AFTER its unshifted equivalent
 * in this table.
#   endif
#endif
 */
#if (KEYALIAS > KEY_SCO)
private Index		FKeyVec[256];
private const char	possible[] =
#else
#   define		FKeyVec	((Index *) possible)
private char		possible[256 * sizeof(Index)] =
#endif
{
/* arrow keys */
  'k','u',  'k','d',  'k','r',  'k','l',
/* function keys 0-10 */
  'k','0',  'k','1',  'k','2',  'k','3',  'k','4',  'k','5',
  'k','6',  'k','7',  'k','8',  'k','9',  'k',';',
/* 3x3 keypad keys */
  'K','1',  'K','2',  'K','3',  'K','4',  'K','5',
/* special keys */
  'k','a',  'k','A',  'k','b','=','\b',   'k','C',  'k','D',  'k','E',
  'k','F',  'k','h',  'k','H',  'k','I',  'k','L',  'k','M',
  'k','N',  'k','P',  'k','R',  'k','S',  'k','T',  'k','t',
/* extra special keys */
  'k','B',  'k','c',  'k','i',  'k','n','=','\r',   'k','o','=','\t',
  'k','p',  'k','q',  'k','Q',  'k','U',  'k','v',  'k','V',
  'k','W',  'k','Y',  'k','z',  'k','Z',
/* extra function keys 11-45 */
  'F','1',  'F','2',  'F','3',  'F','4',  'F','5',  'F','6',
  'F','7',  'F','8',  'F','9',  'F','0',  'F','A',  'F','B',
  'F','C',  'F','D',  'F','E',  'F','F',  'F','G',  'F','H',
  'F','I',  'F','J',  'F','K',  'F','L',  'F','M',  'F','N',
  'F','O',  'F','P',  'F','Q',  'F','R',  'F','S',  'F','T',
  'F','U',  'F','V',  'F','W',  'F','X',  'F','Y',  'F','Z',
/* still more function keys 46-63 */
#if (NFKEYS == 128)
  'F','a',  'F','b',  'F','c',  'F','d',  'F','e',  'F','f',
  'F','g',  'F','h',  'F','i',  'F','j',  'F','k',  'F','l',
  'F','m',  'F','n',  'F','o',  'F','p',  'F','q',  'F','r',
#endif

#if (KEYALIAS & KEY_EXT)
  '@','1', AliasTo 'K','B',		/* beg(inning) */
  '@','2', AliasTo 'k','X',		/* cancel */
  '@','3', AliasTo 'k','x',		/* close */
  '@','4', AliasTo 'k','c',		/* command */
  '@','5', AliasTo 'K','C',		/* copy */
  '@','6', AliasTo 'K','c',		/* create */
  '@','7', AliasTo 'k','H',		/* end */
  '@','8', '=','\r',			/* enter */
  '@','9', AliasTo 'k','Q',		/* exit (quit) */
  '@','0', AliasTo 'k','f',		/* find */
  '%','1', AliasTo 'k','q',		/* help */
  '%','2', AliasTo 'k','k',		/* mark */
  '%','3', AliasTo 'K','M',		/* message */
  '%','4', AliasTo 'k','m',		/* move */
  '%','5', AliasTo 'k','v',		/* next */
  '%','6', AliasTo 'k','O',		/* open */
  '%','7', AliasTo 'K','O',		/* options */
  '%','8', AliasTo 'k','V',		/* previous */
  '%','9', AliasTo 'K','P',		/* print */
  '%','0', AliasTo 'k','i',		/* redo */
  '&','1', AliasTo 'K','f',		/* reference */
  '&','2', AliasTo 'K','F',		/* refresh */
  '&','3', AliasTo 'K','R',		/* replace */
  '&','4', AliasTo 'K','r',		/* restart */
  '&','5', AliasTo 'k','g',		/* resume (go) */
  '&','6', AliasTo 'k','s',		/* save */
  '&','7', AliasTo 'k','G',		/* suspend (!go) */
  '&','8', AliasTo 'k','Y',		/* undo */
#   define WANT_KEYSHIFT
  '&','9', AliasTo 'k','B' shifted,	/* shift-begin */
  '&','0', AliasTo 'k','X' shifted,	/* shift-cancel */
  '*','1', AliasTo 'k','c' shifted,	/* shift-command */
  '*','2', AliasTo 'K','C' shifted,	/* shift-copy */
  '*','3', AliasTo 'K','c' shifted,	/* shift-create */
  '*','4', AliasTo 'k','D' shifted,	/* shift-delete-character */
  '*','5', AliasTo 'k','L' shifted,	/* shift-delete-line */
  '*','6', AliasTo 'k','U',		/* select */
  '*','7', AliasTo 'k','H' shifted,	/* shift-end */
  '*','8', AliasTo 'k','E' shifted,	/* shift-clear-to-eol */
  '*','9', AliasTo 'k','Q' shifted,	/* shift-exit */
  '*','0', AliasTo 'k','f' shifted,	/* shift-find */
  '#','1', AliasTo 'k','q' shifted,	/* shift-help */
  '#','2', AliasTo 'k','h' shifted,	/* shift-home */
  '#','3', AliasTo 'k','I' shifted,	/* shift-insert */
  '#','4', AliasTo 'k','z',		/* shift-left-arrow (word left) */
  '%','a', AliasTo 'K','M' shifted,	/* shift-message */
  '%','b', AliasTo 'K','m' shifted,	/* shift-move */
  '%','c', AliasTo 'k','v' shifted,	/* shift-next */
  '%','d', AliasTo 'K','O' shifted,	/* shift-options */
  '%','e', AliasTo 'k','V' shifted,	/* shift-previous */
  '%','f', AliasTo 'K','P' shifted,	/* shift-print */
  '%','g', AliasTo 'k','i' shifted,	/* shift-redo */
  '%','h', AliasTo 'K','R' shifted,	/* shift-replace */
  '%','i', AliasTo 'k','Z',		/* shift-right-arrow (word right) */
  '%','j', AliasTo 'k','g' shifted,	/* shift-resume */
  '!','1', AliasTo 'k','s' shifted,	/* shift-save */
  '!','2', AliasTo 'k','G' shifted,	/* shift-suspend */
  '!','3', AliasTo 'k','Y' shifted,	/* shift-undo */
#endif /* (KEYALIAS & KEY_EXT) */

#if (KEYALIAS & KEY_SCO)
/*
 * PU,PD,HM,EN are XENIX/SCO termcap specific;  we internally alias these
 * to their "official" termcap equivalents kP,kN,kh,kH so as to keep
 * our .joverc portable.
 */
  'P','U', AliasTo 'k','P',
  'P','D', AliasTo 'k','N',
  'H','M', AliasTo 'k','h',
  'E','N', AliasTo 'k','H',
#endif /* (KEYALIAS & KEY_SCO) */

#if (KEYALIAS & KEY_AIX)
  'k','w', AliasTo 'k','H',
  'k','<', AliasTo 'F','1',
  'k','>', AliasTo 'F','2',
  'k','!', AliasTo 'F','3',
  'k','@', AliasTo 'F','4',
  'k','#', AliasTo 'F','5',
  'k','$', AliasTo 'F','6',
  'k','%', AliasTo 'F','7',
  'k','^', AliasTo 'F','8',
  'k','&', AliasTo 'F','9',
  'k','*', AliasTo 'F','A',
  'k','(', AliasTo 'F','B',
  'k',')', AliasTo 'F','C',
  'k','-', AliasTo 'F','D',
  'k','_', AliasTo 'F','E',
  'k','+', AliasTo 'F','F',
  'k',',', AliasTo 'F','G',
  'k',':', AliasTo 'F','H',
  'k','?', AliasTo 'F','I',
  'k','[', AliasTo 'F','J',
  'k',']', AliasTo 'F','K',
  'k','{', AliasTo 'F','L',
  'k','}', AliasTo 'F','M',
  'k','|', AliasTo 'F','N',
  'k','~', AliasTo 'F','O',
  'k','/', AliasTo 'F','P',
  'S','1', AliasTo 'F','Q',
  'S','2', AliasTo 'F','R',
  'S','3', AliasTo 'F','S',
  'S','4', AliasTo 'F','T',
  'S','5', AliasTo 'F','U',
  'S','6', AliasTo 'F','V',
  'S','7', AliasTo 'F','W',
  'S','8', AliasTo 'F','X',
  'S','9', AliasTo 'F','Y',
  'S','A', AliasTo 'F','Z',
#endif /* (KEYALIAS & KEY_AIX) */
  '\0'
};

#ifndef KEYSHIFT
#   ifdef WANT_KEYSHIFT
#	define KEYSHIFT	1
#   endif
#else
#   if (2 * KEYSHIFT -1 == 2 * -1)	/* defined but empty */
#	define KEYSHIFT	1
#   endif
#endif

private FKEY	*FKeyState;		/* the states */

short		FKeyMap[NFKEYS];	/* IDs of actual function keys */

public int
getchar()
{
	/* buffer must be big enough to hold longest function key sequence */
#	define RINGSIZE	10
	static short	Ringbuf[RINGSIZE];
	static short	*head = Ringbuf;
	static int	navail = 0;

	/* yet another peek buffer. Sigh... */
	static int	peekc = -1;

	register short	*p = head;
	register FKEY	*state;
	register int	c;
	register int	nleft;
	register Index	prev;

	if ((c = peekc) >= 0) {
		peekc = -1;
		return c;
	}
	/* PRE: head not at end of ring buffer */

	if (nleft = navail) {
		--navail;
		c = *p++;
		if (p == &Ringbuf[RINGSIZE])
			p -= RINGSIZE;			/* wraparound */
		head = p;
	} else {
		c = rawchar(NO);
	}
	/*
	 * If function key recognition is disabled, or no state exists for
	 * the current input character, or it is not a valid initial
	 * state, we are ready very soon.
	 */
	if (True(UseFKeys) && (prev = FKeyVec[c]) &&
	    !(state = &FKeyState[UI(prev)])->fk_prev) {
		/*
		 * If it is a final state we are ready now.
		 */
		if (state->fk_final) {
#	if notyet
			/* Return untranslated character if we do not allow
			   single-char function key sequences.  This is so
			   that ordinary JOVE bindings are not overridden. */
			/* {{Should we introduce a new variable for this?
			     If not, we must either change the type of
			     `UseFKeys', or allow assignment of arbitrary
			     integers to V_BOOL variables.}} */
			if (UseFKeys > 1 && c < 0200)
				return c;
#	endif
#	if KEYSHIFT
			goto final_state;
#	else
			return state->fk_final;
#	endif
		}
	    {
		register int	savc = c;		/* remember this char */
#	if FKEYS_DEBUG
		register Buffer	*savb = curbuf;
		SetBuf(do_select((Window *)0, "*DEBUG*"));
		SETBUFTYPE(curbuf, B_SCRATCH);
		ToLast();
#	endif
		do {
			DPRINTF(("(%d)", UI(prev)));

			if (--nleft > 0) {
				c = *p++;
			} else {
				if ((c = rawchar(YES)) < 0)
					break;		/* timeout	*/
				*p++ = c;
				++navail;
			}
			if (p == &Ringbuf[RINGSIZE])
				p -= RINGSIZE;		/* wraparound	*/

			if (c = UI(FKeyVec[c])) do {

				state = &FKeyState[c];

				if (state->fk_prev == prev) {	/* match */

					if (!state->fk_final)
						break;
					/*
					 * If we get here we recognized a key
					 * sequence.  Update the state of our
					 * character buffer and return result.
					 */
					DPRINTF(("(%d)[match: %s%p]\n", c,
						 (state->fk_final & FK_SHIFT) ?
							"*" : NullStr,
						 state->fk_final));
					head = p;
					navail = (--nleft > 0) ? nleft : 0;
#	if FKEYS_DEBUG
					SetBuf(savb);
#	endif
#	if KEYSHIFT
		final_state:
					if ((c = state->fk_final) & FK_SHIFT) {
						peekc = _UC_(c);
						if (!(c = MetaKey))
							c = ESC;
					}
					return c;
#	else
					return state->fk_final;
#	endif
				}
			} while (c = UI(state->fk_alt));
		} while (prev = c);

		DPRINTF(((navail == 0) ? "\n" : "[%d available]\n", navail));

#	if FKEYS_DEBUG
		SetBuf(savb);
#	endif
		c = savc;
	    }
	}
	/*
	 * Handle 8-bit characters here (instead of in rawchar).
	 * {{this used to remember next character by Ungetc() but that
	 *  screwed up keyboard macro definition. We can't push it back on
	 *  our ring buffer either since 8th bit (if still on) would be
	 *  re-interpreted the next time}}
	 */
	if (c & 0200) {
		if (MetaKey) {
#	ifdef META_FKEYS
			/*
			 * This will recognize key sequences with the Meta-bit
			 * ORed into its first (and only the first) character,
			 * and translate it into <Meta> <Fkey>.
			 * Maybe some terminals are capable of doing that.
			 */
			if (p == Ringbuf)	/* wraparound */
				p += RINGSIZE;
			*--p = (c &= 0177);	/* push back into ring buffer */
			head = p;
			navail++;
#	else
			peekc = (c &= 0177);
#	endif
			return MetaKey;
		} else {
			peekc = c;	/* cheat to get literal 8-bit char */
			return QuoteChar;
		}
	}
	return c;
} /* getchar */


public void
FKeyInit() /* to be called (once) when termcap is still available */
{
	char	*keydefs[NFKEYS+1];
	char	keybuf[(NFKEYS+1)*(KEYLEN+1)];
	int	nkeys = 0;
	int	err = 0;

	/* select a buffer where to put our eventual error messages */

	SetBuf(do_select((Window *)0, "*INIT errors*"));

#	define Error(x)	(++err, ins_str(x, NO))

	/* get key definitions from termcap */
    {
#ifdef FKeyVec		/* overlays possible[], so should be cleared */
#   define CLEAR(x)	x = 0
#else			/* no overlay, const possible[], so don't touch */
#   define CLEAR(x)	x
#endif
#if (SIZEOF_SHORT == 2)	/* do some dastardly dirty casts and hope we can get away with it... */
#   define SCAN_T	short
#   define DO(x)	x
#   define GET(s)	(*s)
#else			/* ... but provide a way out if we don't */
#   define SCAN_T	char
#   define DO(x)	x, x
	union { short i; char c[2]; } u;
#   define GET(s)	(u.i = 0, u.c[0] = s[0], u.c[1] = s[1], u.i)
#endif
	register SCAN_T	*s = (SCAN_T *) possible;
	register short	*mp = FKeyMap;
	register char	**vp = keydefs;
	char		*p = keybuf;

	do {
#if KEYALIAS
	    if (!Aliased(s))
#endif
		if (*vp = tgetstr((char *) s, &p)) {
			if (mp == &FKeyMap[NFKEYS]) {
				Error(sprint("FKeyInit: can't handle more than %d keydefs\n", NFKEYS));
				break;
			}
			/* key remap kludge: store remap sequence in FKeyMap */
			if (((char *) s)[2] == '='
#if KEYALIAS
			    || Aliased(&((char *) s)[2])
#endif
			    ) {
				DO(CLEAR(*s++));
			}
			*mp++ = GET(s); /* record key name */
#if FKEYS_DEBUG
		    {
			register char	*t = *vp;
			DPRINTF(("%2d: %s%c%c @%04X ", nkeys,
				 Shifted(s) ? "*" : "", KEYID(nkeys), (long)t));
			while (*t)
				DPRINTF(("\t%02x %p", *t, *t)), t++;
			DPRINTF(("\n"));
		    }
#endif /* FKEYS_DEBUG */
			*vp++;
			nkeys++;
			if (p > &keybuf[sizeof keybuf - KEYLEN - 2]) {
				Error(("FKeyInit: buffer overflow\n"));
				break;
			}
		}
		DO(CLEAR(*s++));

	} while (*(char *) s);

#ifdef FKeyVec
	/* we must still clear the rest of the name table if we encountered an error. */
	while (*(char *) s)
		*s++ = 0;
#endif
    }
	DPRINTF(("FKeyInit: nkeys=%d\n", nkeys));
    {
#	if (0 < MAXSTATE && MAXSTATE < NFKEYS*KEYLEN)
#	    define NSTATES	MAXSTATE
#	else
#	    define NSTATES	NFKEYS*KEYLEN
#	endif
	FKEY		states[NSTATES + 2];
	register FKEY	*last = states + 1;
      {
	register FKEY	*curr;
	register char	*s;
	register int	c;
	register int	ic,		/* curr == &states[ic] */
			il = 1;		/* last == &states[il] */
	register int	key;

	/* build state table from function key definitions */

	for (key = 0;  key < nkeys;  key++) {

		s = keydefs[key];

		DPRINTF(("%s%c%c:\t", Shifted(&FKeyMap[key]) ? "*" : "", KEYID(key)));

		for (last->fk_prev = 0; c = _UC_(*s++); last->fk_prev = ic) {

			last->fk_alt = 0;
			last->fk_final = 0;
			/*
			 * Insert new state at the beginning of the alternates
			 * list for this character if the list is empty,
			 * or this is an initial state and this is not yet
			 * present.
			 */
			if (!(ic = UI(FKeyVec[c])) ||
			    ((curr = &states[ic])->fk_prev && !last->fk_prev)) {
				DPRINTF((ic ? "<" : "+"));
				last->fk_alt = ic;
				FKeyVec[c] = ic = il++;
				curr = last++;
			} else {
				while (curr->fk_prev != last->fk_prev) {
					if (!(ic = curr->fk_alt)) {
						DPRINTF((">"));
						curr->fk_alt = ic = il++;
						curr = last++;
						break;
					}
					curr = &states[ic];
				}
			}
			DPRINTF(("%02x'%c'(%d)\t", c, c, (int)(curr - states)));

			/* sanity checks */
#if (MAXSTATE > 0)
			if (curr == &states[MAXSTATE]) {
				Error(("FKeyInit: too many states\n"));
				goto fail;
			}
#endif
			if (curr->fk_final) {
				Error(sprint("FKeyInit: %p is subsequence of %p\n",
					     curr->fk_final, key + FK_BASE));
				break;
			}
		}
		s = (char *) &FKeyMap[key];	/* key remap kludge */

		curr->fk_final = (*s++ == '=') ? *s : key + FK_BASE;
#if KEYALIAS
		if (Aliased(--s)) {
#   if KEYSHIFT
			register short fk_shift = 0;

			if (Shifted(s)) {
				UnShift(s);
				fk_shift = FK_SHIFT;
			}
#   endif
			UnAlias(s);
			/*
			 * Search keymap for its alias.  If it is there, use
			 * its keynumber instead, and invalidate this entry.
			 */
			for (c = key; --c >= 0; ) {
				if (FKeyMap[c] == *(short *) s) {
					curr->fk_final = c + FK_BASE;
					*(short *) s = 0;
					break;
				}
			}
#   if KEYSHIFT
			curr->fk_final |= fk_shift;
#   endif
		}
#endif /* KEYALIAS */
		DPRINTF(("-> %02x\n", curr->fk_final));
	} /* for */
fail:	;
#if FKEYS_DEBUG
	dump(states, (int)(last - (states + 1)));
	err++;
#endif /* FKEYS_DEBUG */
      }
	/*
	 * Save states to more permanent location.
	 */
      {
	register char	*base = (char *)(states + 1);
	register size_t	siz = (size_t)((char *)last - base);

	last = (FKEY *) emalloc(siz);
	byte_copy(base, (char *)last, siz);
	FKeyState = --last;		/* adjust for base-1 indexing */
      }
    }
	if (!err) {
		kill_buf(curbuf);	/* i.e. our error message buffer */
	}
} /* FKeyInit */

#if FKEYS_DEBUG

private
dump(states, nstates)
FKEY	*states;
int	nstates;
{
	register FKEY	*state;
	register int	c, next;
	int		count = 0;

	DPRINTF(("\n\
Char\tState\tPrevSt\tFinal\n\
----\t-----\t------\t-----\n"));

	for (c = 0;  c < 256;  c++)
		if (next = FKeyVec[c]) {
			DPRINTF(("%02x %c:", c, c));
			do {
				state = &states[UI(next)];
				count++;
				DPRINTF(("\t%5d\t%5d\t%02x %c%p\n",
					 next,
					 state->fk_prev,
					 state->fk_final,
					 state->fk_final & FK_SHIFT ? '*' : ' ',
					 state->fk_final
				       ));
			} while (next = state->fk_alt);
		}
	if (count != nstates)
		DPRINTF(("[state count? expected %d found %d]\n", nstates, count));
}
#endif /* FKEYS_DEBUG */

#endif /* TERMCAP */
#endif /* FUNCKEYS */

/*======================================================================
 * $Log: fkeys.c,v $
 * Revision 14.32.0.12  1994/06/24  17:55:03  tom
 * (FKeyInit): remove redundant decl. of tgetent(), fix name of scratch buffer,
 *  Error: move sprint outside macro definition.
 *
 * Revision 14.32.0.10  1994/05/22  22:22:02  tom
 * (KEYALIAS): set default to KEY_EXT for non-TINY systems.
 *
 * Revision 14.32  1993/07/06  00:53:07  tom
 * (use-function-keys): remove V_TTY_RESET;
 * (FK_BASE): new constant, replace 0200 with FK_BASE where appropriate;
 * (FK_SHIFT): change from 0400 to 01000.
 *
 * Revision 14.31  1993/02/13  04:14:47  tom
 * allow debug options to be specified in "tune.h" cf. "Local.h".
 *
 * Revision 14.30  1993/02/05  00:07:27  tom
 * cleanup whitespace; some random optimizations; standardize DEBUG.
 *
 * Revision 14.28  1992/10/05  13:25:08  tom
 * convert to "port{ansi,defs}.h" conventions.
 *
 * Revision 14.27  1992/09/21  14:54:03  tom
 * replace CTL('Q') with `QuoteChar'; cleanup empty `#' directives.
 *
 * Revision 14.26  1992/08/26  23:56:53  tom
 * PRIVATE-ized some Variable defs; add RCS directives.
 *
 */
