/*
 *  Copyright (c) 1992, 1994 John E. Davis  (davis@amy.tch.harvard.edu)
 *  All Rights Reserved.
 */
#include <stdio.h>
#include <string.h>
#include <setjmp.h>

#ifndef sequent
# include <stdlib.h>
#endif

#include "config.h"
#include "jmalloc.h"
#include "buffer.h"
#include "cmds.h"
#include "paste.h"
#include "screen.h"
#include "window.h"
#include "text.h"
#include "ledit.h"
#include "keymap.h"
#include "sysdep.h"
#include "misc.h"
#include "display.h"
#include "file.h"
#include "slang.h"
#include "undo.h"
#include "ins.h"
#include "mem.h"
#include "hooks.h"

/* We need a define a rule for upperand lower case chars that user cannot
   change!  This could be a problem for international chars! */

#define UPPER_CASE_KEY(x) (((x) >= 'a') && ((x) <= 'z') ? (x) - 32 : (x))
#define LOWER_CASE_KEY(x) (((x) >= 'A') && ((x) <= 'Z') ? (x) + 32 : (x))

void (*X_Define_Keys_Hook)(Key_Type *);

typedef struct
{
   jmp_buf b;
} jmp_buf_struct;

jmp_buf_struct Jump_Buffer, *Jump_Buffer_Ptr;

typedef int (*Jed_fp)(void);

VOID *Last_Key_Function;
int *Repeat_Factor;                    /* repeats a key sequence if non null */

char Key_Buffer[13];
char *Key_Bufferp = Key_Buffer;
int Key_TimeOut_Flag = 0;	       /* true if more than 1 sec has elapsed
                                          without key in multikey sequence */

char *Read_This_Character = NULL;      /* alternate keyboard buffer */

/* special self insert key type */
Key_Type Self_Insert_KeyType;

#ifdef HAS_MOUSE
JMouse_Type JMouse;
#endif

typedef struct Jed_Function_Type
  {
      char *name;
      Jed_fp f;
  }
Jed_Function_Type;

Jed_Function_Type Jed_Functions[] =
  {
      {"backward_delete_char", backward_delete_char_cmd},
      {"backward_delete_char_untabify", backward_delete_char_untabify},
      {"beep", beep},
      {"begin_macro", begin_keyboard_macro},
      {"beg_of_buffer", bob},
      {"beg_of_line", bol},
      {"brace_bra_cmd", brace_bra_cmd},
      {"brace_ket_cmd", brace_ket_cmd},
      {"center_line", center_line},
      {"copy_region", copy_to_pastebuffer},
#if defined(msdos) || defined(__GO32__)
      {"coreleft", show_memory},
#endif
      {"delete_char_cmd", delete_char_cmd},
      {"delete_window", delete_window},
      {"digit_arg", digit_arg},
/*      {"double_line", double_line}, */
      {"end_macro", end_keyboard_macro},
      {"enlarge_window", enlarge_window},
      {"end_of_buffer", eob},
      {"eol_cmd", eol_cmd},
      {"evaluate_cmd", evaluate_cmd},
      {"execute_macro", execute_keyboard_macro},
      {"exchange",exchange_point_mark},
      {"exit_jed", exit_jed},
      {"exit_mini", exit_minibuffer},
      {"find_file", find_file},
      {"format_paragraph", text_format_paragraph},
      {"goto_match", goto_match},
      {"indent_line", indent_line},
      {"insert_file", insert_file_cmd},
      {"kbd_quit", kbd_quit},
      {"kill_buffer", kill_buffer},
      {"kill_line", kill_line},
      {"kill_region", kill_region},
      {"macro_query", macro_query},
      {"mark_spot", mark_spot},
      {"mini_complete", mini_complete},
      {"narrow", narrow_to_region},
      {"narrow_paragraph", narrow_paragraph},
      {"newline", newline},
      {"newline_and_indent", newline_and_indent},
      {"next_char_cmd", next_char_cmd},
      {"next_line_cmd", next_line_cmd},
      {"one_window", one_window},
      {"other_window", other_window},
      {"page_down", pagedown_cmd},
      {"page_up", pageup_cmd},
      {"pop_spot", pop_spot},
      {"previous_char_cmd", previous_char_cmd},
      {"previous_line_cmd", previous_line_cmd},
      {"quoted_insert", quoted_insert},
      {"redraw", redraw},
      /* {"replace", replace}, */
      {"save_buffers", save_some_buffers},
      {"scroll_left", scroll_left},
      {"scroll_right", scroll_right},
       /* {"search_backward", search_backward_cmd},
	  {"search_forward", search_forward_cmd}, */
      {"self_insert_cmd", ins_char_cmd},
      {"set_mark_cmd", set_mark_cmd},
      {"split_window", split_window},
      {"switch_to_buffer", get_buffer},
      {"sys_spawn_cmd", sys_spawn_cmd},
      {"text_smart_quote", text_smart_quote},
/*       {"transpose_lines", transpose_lines}, */
      {"trim_whitespace", trim_whitespace},
     {"undo", undo},
      {"widen", widen},
      {"write_buffer", write_buffer},
      {"yank", yank},
      {(char *) NULL, NULL}
   };

Key_Type Global_Map[128];
Key_Type *Mini_Map = Global_Map;

#define MAX_KEYMAP_NAME_LEN 8
typedef struct KeyMap_List_Type
{
   char name[MAX_KEYMAP_NAME_LEN + 1];
   Key_Type *keymap;
}
KeyMap_List_Type;

/* This is arbitrary but I have got to start somewhere */
#ifdef msdos
#define MAX_KEYMAPS 10
#else
#define MAX_KEYMAPS 30
#endif

KeyMap_List_Type KeyMap_List[MAX_KEYMAPS];   /* these better be inited to 0! */

Key_Type *malloc_key(unsigned char *str)
{
   Key_Type *neew;

   if (NULL == (neew = (Key_Type *) MALLOC(sizeof(Key_Type))))
     {
	exit_error("malloc_key: malloc error", 0);
     }
   MEMCPY((char *) neew->str, (char *) str, (int) *str);
   return(neew);
}

int kbd_quit(void)
{
   int sle = SLang_Error;
   KeyBoard_Quit = 1;
   
   /* --- never serious, testing only
   if (Repeat_Factor != NULL) 
     {
	msg_error("Bang!");
	longjmp(Jump_Buffer_Ptr->b, 1);
     } */
   
   msg_error("Quit!");
   if (Ignore_User_Abort) SLang_Error = sle;
   return(1);
}

void add_keymap(char *name, Key_Type *map)
{
   int i;

   for (i = 0; i < MAX_KEYMAPS; i++)
     {
	if (KeyMap_List[i].keymap == NULL)
	  {
	     KeyMap_List[i].keymap = map;
	     MEMCPY((char *) KeyMap_List[i].name, (char *) name, MAX_KEYMAP_NAME_LEN);
	     KeyMap_List[i].name[MAX_KEYMAP_NAME_LEN] = 0; 
	     return;
	  }
     }
   msg_error("Keymap quota exceeded");
}

VOID *find_function(char *name)
{
   Jed_Function_Type *fp;

   fp = Jed_Functions;

   while((fp != NULL) && (fp->name != NULL))
     {
	if ((*fp->name == *name) && !strcmp(fp->name, name)) return((VOID *) fp->f);
	fp++;
     }
    return(NULL);
}

/* convert things like "^A" to 1 etc... The 0th char is the strlen */
char *process_keystring(char *s)
{
   static char str[30];
   unsigned char ch;
   int i;

    i = 1;
    while (*s != 0)
      {
          ch = (unsigned char) *s++;
          if (*s) ch = UPPER_CASE_KEY(ch);
          if (ch == '^')
            {
                ch = *s++;
                if (ch == 0)
                  {
                      str[i++] = '^';
                      break;
                  }
	       if (ch == '?') ch = 127; else ch = ch - 'A' + 1;
            }

          str[i++] = ch;
      }
    str[0] = i;
    return(str);
}


static char *Define_Key_Error = "Inconsistency in define key.";

void define_key1(char *s, VOID *f, char type, Key_Type *keymap)
{
   int cmp, i, m, n, len;
   Key_Type *key, *last, *neew;
   unsigned char *str;

   str = (unsigned char *) process_keystring(s);
   if (1 == (n = str[0])) return;

   i = *(str + 1);
   key = (Key_Type *) &(keymap[i]);

   if (n == 2)
     {
	if (key->next != NULL)
	  {
	     msg_error(Define_Key_Error);
	     return;
	  }
	
	/* copy keymap uses the pointers so do not free these.
	if (key->type == F_INTERPRET) FREE(key->f); */
	
	key->f = f;
	key->type = type;
	MEMCPY((char *) key->str, (char *) str, 2);
	return;
     }

   /* insert the key definition */
   while(1)
     {
	last = key;
	key = key->next;
	if ((key == NULL) || (key->str == NULL)) cmp = -1;
	else
	  {
	     m = key->str[0];
	     len = m;
	     if (m > n) len = n;
	     cmp = MEMCMP((char *) (str + 1), (char *)(key->str + 1), len - 1);
	     if ((m != n) && !cmp)
	       {
		  msg_error(Define_Key_Error);
		  return;
	       }
	     if ((m == n) && (!cmp)) cmp = MEMCMP((char *) str, (char *) key->str, n);
	  }

	if (cmp == 0)
            {
	       /* I do not dare do this since copy keymap keeps the pointers 
	       if (key->type == F_INTERPRET) FREE(key->f); */
	       key->f = f;
	       key->type = type;
	       return;
            }
          else if (cmp < 0)
            {
	       neew = malloc_key(str);

	       neew -> next = key;
	       last -> next = neew;

	       neew->f = f;
	       neew->type = type;
	       return;
            } /* else cmp > 0 */
      }
}

void define_key(char *s, char *funct, Key_Type *keymap)
{
   VOID *f;
   char type;

   f = find_function(funct);
   if (f == NULL)                      /* assume interpreted */
     {
	if (NULL == (f = (VOID *) MALLOC(strlen(funct) + 1)))
	  {
	     exit_error("Malloc error in define key.", 0);
	  }

	strcpy((char *) f, funct);
	type = F_INTERPRET;
     }
   else type = F_INTRINSIC;

   define_key1(s, f, type, keymap);
}

void init_keymaps(void)
{
    char simple[2], ch;
    simple[1] = 0;

    for (ch = ' '; ch < 127; ch++)
      {
          simple[0] = ch;
          define_key1(simple, (VOID *) ins_char_cmd, F_INTRINSIC, Global_Map);
      }
   

    define_key1("^[1", (VOID *) digit_arg, F_INTRINSIC, Global_Map);
    define_key1("^[2", (VOID *) digit_arg, F_INTRINSIC, Global_Map);
    define_key1("^[3", (VOID *) digit_arg, F_INTRINSIC, Global_Map);
    define_key1("^[4", (VOID *) digit_arg, F_INTRINSIC, Global_Map);
    define_key1("^[5", (VOID *) digit_arg, F_INTRINSIC, Global_Map);
    define_key1("^[6", (VOID *) digit_arg, F_INTRINSIC, Global_Map);
    define_key1("^[7", (VOID *) digit_arg, F_INTRINSIC, Global_Map);
    define_key1("^[8", (VOID *) digit_arg, F_INTRINSIC, Global_Map);
    define_key1("^[9", (VOID *) digit_arg, F_INTRINSIC, Global_Map);
    define_key1("^[0", (VOID *) digit_arg, F_INTRINSIC, Global_Map);

#ifndef pc_system
   /* give vtxxx arrow keys */
    define_key1("^[[D", (VOID *) previous_char_cmd,F_INTRINSIC, Global_Map);
    define_key1("^[[A", (VOID *) previous_line_cmd, F_INTRINSIC, Global_Map);
    define_key1("^[[B", (VOID *) next_line_cmd, F_INTRINSIC, Global_Map);
    define_key1("^[[C", (VOID *) next_char_cmd, F_INTRINSIC, Global_Map);
    define_key1("^[[6~", (VOID *) pagedown_cmd, F_INTRINSIC, Global_Map);
    define_key1("^[[5~", (VOID *) pageup_cmd, F_INTRINSIC, Global_Map);
    define_key1("^K^[[C", (VOID *) scroll_left, F_INTRINSIC, Global_Map);
    define_key1("^K^[[D", (VOID *) scroll_right, F_INTRINSIC, Global_Map);
    define_key1("^K^[[A", (VOID *) bob, F_INTRINSIC, Global_Map);
    define_key1("^K^[[B", (VOID *)eob, F_INTRINSIC, Global_Map);
#else
#include "doskeys.c"
#endif

    define_key1("'", (VOID *) text_smart_quote, F_INTRINSIC, Global_Map);
    define_key1("\"", (VOID *) text_smart_quote, F_INTRINSIC, Global_Map);
    define_key1("^_", (VOID *) undo, F_INTRINSIC, Global_Map);
    define_key1("^?", (VOID *) backward_delete_char_untabify, F_INTRINSIC, Global_Map);
    define_key1("^B", (VOID *) bol, F_INTRINSIC, Global_Map);
    define_key1("^D", (VOID *) pagedown_cmd, F_INTRINSIC, Global_Map);
    define_key1("^E", (VOID *) eol_cmd, F_INTRINSIC, Global_Map);
/*
    define_key1("^FB", (VOID *) search_backward_cmd, F_INTRINSIC, Global_Map);
    define_key1("^FF", (VOID *) search_forward_cmd, F_INTRINSIC, Global_Map);
*/
    define_key1("^G", (VOID *) kbd_quit, F_INTRINSIC, Global_Map);
    define_key1("^I", (VOID *) indent_line, F_INTRINSIC, Global_Map);
    define_key1("^K(", (VOID *) begin_keyboard_macro, F_INTRINSIC, Global_Map);
    define_key1("^K)", (VOID *) end_keyboard_macro, F_INTRINSIC, Global_Map);
    define_key1("^KD", (VOID *) evaluate_cmd, F_INTRINSIC, Global_Map);
    define_key1("^KE", (VOID *) exit_jed, F_INTRINSIC, Global_Map);
    define_key1("^KG", (VOID *) find_file, F_INTRINSIC, Global_Map);
    define_key1("^KK", (VOID *) copy_to_pastebuffer, F_INTRINSIC, Global_Map);
    define_key1("^KM", (VOID *) mark_spot, F_INTRINSIC, Global_Map);
    define_key1("^KX", (VOID *) execute_keyboard_macro, F_INTRINSIC, Global_Map);
    define_key1("^K^B", (VOID *) set_mark_cmd, F_INTRINSIC, Global_Map);
    define_key1("^K^I", (VOID *) insert_file_cmd, F_INTRINSIC, Global_Map);
/*     define_key1("^K^L", (VOID *) double_line, F_INTRINSIC, Global_Map); */
    define_key1("^K^M", (VOID *) pop_spot, F_INTRINSIC, Global_Map);
    define_key1("^K^P", (VOID *) yank, F_INTRINSIC, Global_Map);
/*     define_key1("^K^R", (VOID *) replace, F_INTRINSIC, Global_Map); */
    define_key1("^K^V", (VOID *) kill_region, F_INTRINSIC, Global_Map);
    define_key1("^K^W", (VOID *) write_buffer, F_INTRINSIC, Global_Map);
    define_key1("^L", (VOID *) kill_line, F_INTRINSIC, Global_Map);
    define_key1("^M", (VOID *) newline_and_indent, F_INTRINSIC, Global_Map);
    define_key1("^R", (VOID *) redraw, F_INTRINSIC, Global_Map);
    define_key1("^U", (VOID *) pageup_cmd, F_INTRINSIC, Global_Map);
    define_key1("^V", (VOID *) delete_char_cmd, F_INTRINSIC, Global_Map);
    define_key1("^W0", (VOID *) delete_window, F_INTRINSIC, Global_Map);
    define_key1("^W1", (VOID *) one_window, F_INTRINSIC, Global_Map);
    define_key1("^W2", (VOID *) split_window, F_INTRINSIC, Global_Map);
    define_key1("^WO", (VOID *) other_window, F_INTRINSIC, Global_Map);
    define_key1("^XB", (VOID *) get_buffer, F_INTRINSIC, Global_Map);
    define_key1("^XK", (VOID *) kill_buffer, F_INTRINSIC, Global_Map);
    define_key1("^XN", (VOID *) narrow_to_region, F_INTRINSIC, Global_Map);
    define_key1("^XQ", (VOID *) macro_query, F_INTRINSIC, Global_Map);
    define_key1("^XS", (VOID *) save_some_buffers, F_INTRINSIC, Global_Map);
    define_key1("^XW", (VOID *) widen, F_INTRINSIC, Global_Map);
    define_key1("^X^", (VOID *) enlarge_window, F_INTRINSIC, Global_Map);
/*     define_key1("^X^T", (VOID *) transpose_lines, F_INTRINSIC, Global_Map); */
   define_key1("^X^[", (VOID *) evaluate_cmd, F_INTRINSIC, Global_Map);
    define_key1("^X^X", (VOID *) exchange_point_mark, F_INTRINSIC, Global_Map);
    define_key1("^Z", (VOID *) sys_spawn_cmd, F_INTRINSIC, Global_Map);
    define_key1("^[<", (VOID *) bob, F_INTRINSIC, Global_Map);
    define_key1("^[>", (VOID *) eob, F_INTRINSIC, Global_Map);
    define_key1("^[N", (VOID *) narrow_paragraph, F_INTRINSIC, Global_Map);
    define_key1("^[Q", (VOID *) text_format_paragraph, F_INTRINSIC, Global_Map);
    define_key1("^[S", (VOID *) center_line, F_INTRINSIC, Global_Map);
    define_key1("^[X", (VOID *) evaluate_cmd, F_INTRINSIC, Global_Map);
    define_key1("^[\\", (VOID *) trim_whitespace, F_INTRINSIC, Global_Map);
    define_key1("^\\", (VOID *) goto_match, F_INTRINSIC, Global_Map);
    define_key1("`", (VOID *) quoted_insert, F_INTRINSIC, Global_Map);
    define_key1("{", (VOID *) brace_bra_cmd, F_INTRINSIC, Global_Map);
    define_key1("}", (VOID *) brace_ket_cmd, F_INTRINSIC, Global_Map);

   if (X_Define_Keys_Hook != NULL)  (*X_Define_Keys_Hook)(Global_Map);
   add_keymap("global", Global_Map);
}

int key_interpret(Key_Type *key)
{
   char *rtc_save;
   char *str;
   int ret, ch;
   
   if (key->type == F_INTRINSIC)
     {
	ret = (int) ((Jed_fp ) key->f)();
     }
   else
     {
	str = (char *) key->f;
	
	if (*str == ' ')
	  {
	     /* string to insert */
	     insert_string(str + 1);
	  }
	else if ((*str == '@') && (0 != *(str + 1)))
	  {
	     rtc_save = Read_This_Character;
	     
	     Read_This_Character = str + 1;
	     do
	       {
		  ch = jed_getkey();
		  do_key((char) ch);
	       }
	     while (Read_This_Character != NULL);
	     Read_This_Character = rtc_save;
	  }
	
	else if (!SLang_execute_function(str)) SLang_load_string(str);
	ret = 1;
     }
   
   Last_Key_Function = key->f;
   return(ret);
}

#define XKEY(key)  key_interpret((key))

Key_Type *do_key2(char ch)
{
   register Key_Type *key, *next;
   int i;
   unsigned char ch1; 
   register unsigned char chi, chup, chlow;

   Last_Command_Char = (int) ch;

   i = ch1 = (unsigned char) ch;

   /* if metachar is -1 and char has hi bit set then it self inserts. */
   if ((Meta_Char == -1) && (i > 127))
     {
	return(&Self_Insert_KeyType);
     }

   i = i & 0x7F;

   key = (Key_Type *) &((CBuf->keymap)[i]);

   /* if the next one is null, then we know this is it. */
   if (key->next == NULL) 
     {
	/* check its uppercase counterpart if null and return it */
	if ((key->f == NULL) && (i >= 'a') && (i <= 'z')) key = (Key_Type *) &((CBuf->keymap)[i - 32]);
	return(key);
     }
   
   key = key->next;
   Key_TimeOut_Flag = 1;
   Last_Command_Char = jed_getkey();
   
   i = 2;			       /* 0 is keylen, 1 matches, so 2 */
   ch1 = (unsigned char) Last_Command_Char;
   chup = UPPER_CASE_KEY(ch1); chlow = LOWER_CASE_KEY(ch1);

   while(key != NULL)
     {
	if (KeyBoard_Quit) break;
	chi = key->str[i];
	if ((chup == chi) || (chlow == chi))
	  {
	     if (*key->str == i + 1)
	       {
		  /* look for exact match if possible */
		  if (ch1 != chi)
		    {
		       next = key->next;
		       while (next != NULL)
			 {
			    if (next->str[i] == ch1)
			      {
				 if (next->str[0] == i + 1) return(next);
				 break;
			      }
			    next = next->next;
			 }
		    }
		  return(key);
	       }

	     /* before reading a new key, check to see if it is lowercase
	        and try to find a match for it.  Note that this works because
	        only the LAST character in a key sequence can be lowercase */

	     else if ((ch1 == chlow) && (ch1 != chup))
	       {
		  next = key->next;
		  /* look for the lowercase one */
		  while (next != NULL)
		    {
                        if ((next->str[i] == chlow) &&
                            (next->str[0] == i + 1)) return(next);
		       next = next->next;
		    }
	       }

	     Last_Command_Char = ch1 = jed_getkey();
	     i++;
	     chup = UPPER_CASE_KEY(ch1);
	     chlow = LOWER_CASE_KEY(ch1);
	     continue;
	  }
	else if (chlow < chi) break;  /* not found */

	/* else */
	key = key->next;
     }
   /* not found */
   return(NULL);
}

static int do_key1(char ch)
{
   Key_Type *key;
   int repeat;

   Key_TimeOut_Flag = 0;
   key = do_key2(ch);
   if ((key != NULL) && (key->f != NULL))
     {

	if (Repeat_Factor == NULL) return XKEY(key);
	repeat = *Repeat_Factor;
	Suspend_Screen_Update = 1;

	/* some routines may use the repeat factor as a prefix argument */
	while (repeat-- > 0)
	  {
	     if (KeyBoard_Quit || (*Error_Buffer) ||
		 (Repeat_Factor == NULL)) break;
	 /* (!Executing_Keyboard_Macro && (Repeat_Factor == NULL))) break; */
	     XKEY(key);
	  }
	Repeat_Factor = NULL;

	return(1);
     }
   else if (!Executing_Keyboard_Macro && !KeyBoard_Quit)
     {
	beep();
	flush_input();
     }
   if (KeyBoard_Quit) kbd_quit();
   return(0);
}

int do_key(char ch)
{
   int ret;
   ret = do_key1(ch);
   
   /* keys get buffered up in jed_getkey.  This just resets the pointer. */
   if (!Executing_Keyboard_Macro)
     {
	Key_Bufferp = Key_Buffer;
	*Key_Bufferp = 0;
     }
   return ret;
}



void do_jed(void)
{
   char key;
   char *name;
   Buffer *tthis = CBuf;
   
   Key_TimeOut_Flag = 0;
   /* keys get buffered up in jed_getkey.  This just resets the pointer.
      This one is here because we might be calling this routine from the 
      minibuffer and might not have hit the same block in do_key(). 
      */
   if (!Executing_Keyboard_Macro)
     {
	Key_Bufferp = Key_Buffer;
	*Key_Bufferp = 0;
     }
   key = jed_getkey();
   Last_Command_Char = (int) key;

   /* Mark Undo boundary now because tthis might not be valid later */
   mark_undo_boundary(tthis);
   Repeat_Factor = NULL;

   if (do_key(key)) JWindow->trashed = 1;

    /* internal editing commands may have selected a different buffer
       so put it back. */
    if (CBuf != JWindow->buffer)
      {
	 if (buffer_exists(JWindow->buffer)) name = JWindow->buffer->name; else name = "*scratch*";
	 switch_to_buffer_cmd(name);
      }
}

void jed(void)
{

   Self_Insert_KeyType.f = (VOID *) ins_char_cmd;
   Self_Insert_KeyType.type = F_INTRINSIC;
   Self_Insert_KeyType.next = NULL;

   /* This routine is called from main.  So before actually strting, check
      one more hook to just to make sure  all things are go. */
   SLang_run_hooks("jed_startup_hook", NULL, NULL);

   Jump_Buffer_Ptr = &Jump_Buffer;
   
   if (setjmp(Jump_Buffer.b) != 0)
     {
	SLang_restart(1);   /* just in case */
     }

   if (CBuf != JWindow->buffer)
     {
	switch_to_buffer(JWindow->buffer);
	window_buffer(CBuf);
     }
   JWindow->trashed = 1;
   update((Line *) NULL, 0, 0);
   Read_This_Character = NULL;
   while(1)
     {
	Suspend_Screen_Update = 0;
	do_jed();
	if (!KeyBoard_Quit && (CBuf->flags & BUFFER_TRASHED)
	    && (!Cursor_Motion)) CBuf->hits += 1;
	if (CBuf->hits > User_Vars.max_hits)
	  {
	     auto_save_buffer(CBuf);
	     check_buffers();   /* check files on disk to see if they are recent */
	  }
	
	if ((Last_Key_Function != (VOID *) ins_char_cmd)
	    || (JWindow->trashed) || (JWindow != JWindow->next) ||
	    (Mini_Ghost) || (*Message_Buffer) || (*Error_Buffer))
	  {
	     update((Line *) NULL, 0, 0);
	  }
	
     }
}

int digit_arg(void)
{
    static int repeat;
    char buf[20], key;
    int i;

    i = 0;
    buf[i++] = (char) Last_Command_Char;
    while(1)
      {
	 buf[i] = 0;
	 /* message(buf); */
	 /*
	 if (!input_pending(&Number_Zero) && !Executing_Keyboard_Macro)
	   {
	      flush_message(buf);
	   }
	      */
	 Key_TimeOut_Flag = 1;
	 key = jed_getkey();
	 if ((key < '0') || (key > '9')) break;
	 buf[i++] = key;
      }
   /*
   if (!input_pending(&Number_Zero) && !Executing_Keyboard_Macro)
     {
	flush_message(buf);
     } */
   repeat = atoi(buf);
   Repeat_Factor = &repeat;
   do_key(key);
   return(1);
}

void undefine_key(char *s, Key_Type *keymap)
{
   int n, i;
   Key_Type *key, *next, *last, *key_root;
   unsigned char *str;

   str = (unsigned char *) process_keystring(s);

   if (0 == (n = *str++ - 1)) return;
   i = *str;
   key = key_root = (Key_Type *) &(keymap[i]);

   last = key_root;
   key = key_root->next;
   while (key != NULL)
     {
	next = key->next;
	if (!MEMCMP((char *)(key->str + 1), (char *) str, n))
	  {
	     /* I do not do this since copy_keymap does not malloc new ones 
	     if (key->type == F_INTERPRET) FREE(key->f); */
	     FREE(key);
	     last->next = next;
	  }
	else last = key;
	key = next;
     }

   if (n == 1)
     {
	*key_root->str = 0;
	key_root->f = NULL;
	key_root->type = 0;
     }
}

char *make_keystring(unsigned char *s)
{
   static char buf[80];
   char *b;
   int n;

   b = buf;
   n = *s++ - 1;
   while (n--)
     {
	if (*s < 32)
	  {
	     *b++ = '^';
	     *b++ = *s + 'A' - 1;
	  }
	else *b++ = *s;
	s++;
     }
   *b = 0;
   return(buf);
}

int which_key(char *f)
{
   int num = 0, i;
   Key_Type *key, *key_root;
   VOID *fp;
   char type;
   unsigned char buf[5];
   
   if (NULL == (fp = (VOID *) find_function(f))) type = F_INTERPRET;
   else type = F_INTRINSIC;

   i = 128;
   key_root = (Key_Type *) CBuf->keymap;
   while (i--)
     {
	key = key_root->next;
	if ((key == NULL) && (type == key_root->type) &&
	    (((type == F_INTERPRET) && (!strcmp((char *) f, (char *) key_root->f)))
	    || ((type == F_INTRINSIC) && (fp == key_root->f))))
	  {
	     *buf = 2;
	     *(buf + 1) = 128 - 1 - i;
	     SLang_push_string(make_keystring(buf));
	     num++;
	  }
	while (key != NULL)
	  {
	     if ((key->type == type) &&
		 (((type == F_INTERPRET) && (!strcmp((char *) f, (char *) key->f)))
		 || ((type == F_INTRINSIC) && (fp == key->f))))
	       {
		  SLang_push_string(make_keystring(key->str));
		  num++;
	       }
	     key = key->next;
	  }
	key_root++;
     }
   return(num);
}


static char *find_function_string (VOID *f)
{
   Jed_Function_Type *fp;

   fp = Jed_Functions;

   if (f == (VOID *) ins_char_cmd) return "self_insert_cmd";
   
   while((fp != NULL) && (fp->name != NULL))
     {
	if ((VOID *) fp->f == f) return fp->name;
	fp++;
     }
   return NULL;
}


static void dump_this_binding (unsigned char *s, VOID *f, int type)
{
   unsigned char ch;
   char *str,  ctrl[2];
   char *fun;
   int n, len;
   
   ctrl[0] = '^';
   len = *s++ - 1;;
   
   while (len-- > 0)
     {
	n = 1;
        ch = *s++;
	if (ch == 127) 
	  {
	     str = "DEL"; n = 3;
	  }
	else if (ch > ' ') str = (char *) &ch;
	else if (ch == 27) 
	  {
	     str = "ESC";
	     n = 3;
	  }
	else if (ch == ' ')
	  {
	     str = "SPACE";
	     n = 5;
	  }
	else if (ch == '\t')
	  {
	     str = "TAB"; n = 3;
	  }
	else
	  {
	     str = ctrl;
	     *(str + 1) = ch + '@';
	     n = 2;
	  }
	quick_insert((unsigned char *) str, n);
	ins(' ');
     }
   ins_char_n_times('\t', 3);
   
   if (type == F_INTRINSIC)
     {
   	fun = find_function_string (f);
     }
   else fun = (char *) f;

   insert_string (fun);
   newline();
}

void  dump_bindings(char *map)
{
   int i;
   Key_Type *next, *key_root;

   if (NULL == (key_root = find_keymap(map)))
     {
	msg_error("Keymap undefined.");
	return;
     }
   
   for (i = 0; i < 128; i++)
     {
	next = key_root->next;
	if (next != NULL)
	  {
	     while (next != NULL)
	       {
		  dump_this_binding (next->str, next->f, next->type);
		  next = next->next;
	       }
	  }
	else if (key_root->f != NULL) dump_this_binding (key_root->str, key_root->f, key_root->type);
	key_root++;
     }
}


Key_Type *copy_keymap(Key_Type *km)
{
   int i;
   Key_Type *neew, *old, *new_root;

   if (NULL == (new_root = (Key_Type *) CALLOC(128, sizeof(Key_Type))))
     {
	exit_error("Malloc error in copy keymap.", 0);
     }

   for (i = 0; i < 128; i++)
     {
	old = &(km[i]);
	neew = &(new_root[i]);
	neew->f = old->f;
	neew->type = old->type;
	MEMCPY((char *) neew->str, (char *) old->str, (int) *old->str);

	old = old->next;
	while (old != NULL)
	  {
	     neew->next = malloc_key((unsigned char *) old->str);
	     neew = neew->next;
	     neew->f = old->f;
	     neew->type = old->type;
	     old = old->next;
	  }
	neew->next = NULL;
     }
   return(new_root);
}


char *find_key(int *ret)
{
   char *fstr = NULL;
   Key_Type *key;

   *ret = 0;
   Key_TimeOut_Flag = 0;
   Key_Bufferp = Key_Buffer;
   if (NULL == (key = do_key2(jed_getkey()))) return(NULL);
   if (key->type == F_INTRINSIC)
     {
	*ret = 1;
	fstr = find_function_string (key->f);
     }
   else fstr = (char *) key->f;

   return(fstr);
}

Key_Type *create_keymap(char *name)
{
   Key_Type *neew;
   if (NULL != (neew = copy_keymap(Global_Map))) add_keymap(name, neew);
   return neew;
}

Key_Type *find_keymap(char *name)
{
   int i;

   for (i = 0; i < MAX_KEYMAPS; i++)
     {
	if (!strncmp(KeyMap_List[i].name, name, 8)) return KeyMap_List[i].keymap;
     }
   return(NULL);
}

void use_keymap(char *name)
{
   Key_Type *map;
   if (NULL == (map = find_keymap(name)))
     {
	msg_error("Unknown keymap.");
     }
   else (CBuf->keymap = map);
}

char *what_keymap()
{
   int i;

   for (i = 0; i < MAX_KEYMAPS; i++)
     {
	if (CBuf->keymap == KeyMap_List[i].keymap) return KeyMap_List[i].name;
     }
   return "";
}

void set_abort_char(int *c)
{
   char str[2];
   Key_Type *map;
   int i;
#ifdef pc_system
   char ch, *s, *scan = "@#$%^&*()_+?IQWERTYUIOP[]!!ASDFGHJKL!!!!\\ZXCVBNM";
   
   ch = 64 + (char) *c;
   s = scan; while (*s && (*s != ch)) s++;
   if (*s == 0) return;
   Abort_Char = (int) (s - scan) + 0x03;
#else
   Abort_Char = *c;
#endif
   reset_tty();
   init_tty();
   str[0] = *c; str[1] = 0;
    
   for (i = 0; i < MAX_KEYMAPS; i++)
     {
	if ((map = KeyMap_List[i].keymap) == NULL) continue;
	undefine_key(str, map);
	define_key1(str, (VOID *) kbd_quit, F_INTRINSIC, map);
     }
}


static Jed_Function_Type *Flist_Context;
static int Flist_Context_Len;

#define MAX_USER_FLIST 100
static char *Slang_Functions[MAX_USER_FLIST];
static char **Slang_Flist_Context;


int next_function_list(char *buf)
{
   Jed_Function_Type *tthis;
   char *name, **max;
   
   while (1)
     {
	tthis = Flist_Context;
	name = tthis->name;
	if (name == NULL) break;
	Flist_Context++;
	if (!Flist_Context_Len || !strncmp(buf, name, Flist_Context_Len))
	  {
	     strcpy(buf, name);
	     return(1);
	  }
     }
   
   max = Slang_Functions + MAX_USER_FLIST;
   while (Slang_Flist_Context < max)
     {
	name = *Slang_Flist_Context;
	if (name == NULL) return(0);
	name++;  /* skip past hash mark */
	Slang_Flist_Context++;
	if (!Flist_Context_Len || !strncmp(buf, name, Flist_Context_Len))
	  {
	     strcpy(buf, name);
	     return(1);
	  }
     }
   return(0);
}

int open_function_list(char *buf)
{
   Flist_Context = Jed_Functions;
   Slang_Flist_Context = Slang_Functions;
   Flist_Context_Len = strlen(buf);
   return next_function_list(buf);
}

void add_to_completion(char *name)
{
   char **p, *n;
   static char **last = Slang_Functions;
   
   n = SLang_find_name(name);
   if (n == NULL)
     {
	msg_error("Undefined Symbol.");
	return;
     }
   
   p = last;  /* Slang_Functions; */
   
   while (p < Slang_Functions + MAX_USER_FLIST)
     {
	if (*p == NULL) 
	  {
	     *p = n;
	     last = ++p;
	     return;
	  }
	p++;
     }
   msg_error("Completion Quota Exceeded.");
}

int is_internal(char *f)
{
   if (NULL == find_function(f)) return(0);
   return 1;
}
