#include<stdio.h>
 
/* 




                 VV                   VV    EEEEEEEEEEEEEE   
                  VV                 VV     EE  
                   VV               VV      EE
                    VV             VV       EE
                     VV           VV        EE
                      VV         VV         EEEEEEEEEEEEEE
                       VV       VV          EE
                        VV     VV           EE
                         VV   VV            EE
                          VV VV             EE
                           VVV              EE
                            V               EEEEEEEEEEEEEE
                                                       

 
 
 
                       MODULE  : kbdif.c
 
                       VERSION : v3.5
 
                       REVISED : 13 Jun 2006
 
                       AUTHOR  : Michael A. Campbell
 
 
 
 
 
 


                          FUNCTIONAL DESCRIPTION
                          ----------------------
 
               This module contains VE's keyboard interface routines. All
 
               keyboard input passes through the procedures here defined.


               THIS MODULE USES NCURSES CALLS (getch) AND AS SUCH IS NOT

               COMPLETELY PORTABLE. THIS MODULE, TERMIF.C AND MISCCMD.C ARE
               
               THE ONLY MODULES OF VE THAT ARE NOT FULLY PORTABLE.
 
                                                     
 
*/

/* .page */

/*****************
 * INCLUDE FILES *
 *****************/

#include <string.h>
#include <curses.h>

#include "../def/common.def"
#include "../def/cmdix.def"
#include "../def/kbdif.def"

#define MAX_KEYS_PER_STATE      14
#define NUM_ESC_SEQ_STATES      6

#define KS_ESC                  0
#define KS_ESC_4F               1
#define KS_ESC_5B               2
#define KS_ESC_5B_31            3
#define KS_ESC_5B_32            4
#define KS_ESC_5B_5B            5

#define KS_COMPLETE             0x55

#define RESOLVE_LAST_KEY        0x192

#include "../str/vestruct.str"

#include "../inc/vedbase.inc"
#include "../inc/termif.inc"

#include "../ext/strprim.ext"

#include "../ext/kbdif.ext"
#include "../ext/termif.ext"

/* .page *

/********************
 * PUBLIC VARIABLES *
 ********************/

int init_macro[32];        /* Initialization macro, if defined        */
int init_macro_keys = 0;   /* Number of keys in the init macro        */
int init_macro_ix;         /* Index of next key to use                */

/* Define control codes read from the terminal - At this point in     */
/* VE's development, these vars are no longer used as key inputs, but */
/* only as direction variables for the home command. Their specific   */
/* values are no longer relevant, as long as they are unique.         */

char rcu        = 0x48;                     /* cursor up              */
char rcd        = 0x50;                     /* cursor down            */
char rcl        = 0x4b;                     /* cursor left            */
char rcr        = 0x4d;                     /* cursor right           */

/* Define command preamble. This is the key that the OS precedes ALT  */
/* commands with. For example, pressing ALT-a results in a key        */
/* sequence of cmd_pr and "a".                                        */

char cmd_pr     = 0x1b;                    /* xterm command preamble  */

/* .page */

/* VE keyboard commands.  This table is used to set up the execution of a   */
/* command procedure by index, from a table.  This table must agree in both */
/* order and content with the cmd_proc table in the file VEDBASE.C *and*    */
/* the index file ../def/cmdix.def. Note that NUM_CMDS in ../def/common.def */
/* must match the number of entries in this table.                          */

char commands [] = {
    'I',       /* INSERT mode                 ESC-I or Insert Key */
    'O',       /* OVERWRITE mode              ESC-O or Insert Key */

    'a',       /* AGAIN command               ESC-a or F1         */
    'b',       /* BLOCK command               ESC-b or F2         */
    'd',       /* DELETE LEFT command         ESC-d               */
    'e',       /* EDIT command                ESC-e or F8         */
    'f',       /* FIND command                ESC-f or F5         */
    'g',       /* GOTO FILE command           ESC-g or Keypad '-' */
    'h',       /* HELP command                ESC-h               */
    'i',       /* INFO command                ESC-i or Keypad '*' */
    'j',       /* JUMP command                ESC-j or F4         */
    'l',       /* Delete Line command         ESC-l or F9         */
    'm',       /* MACRO command               ESC-m               */
    'n',       /* NEXT FILE command           ESC-n or Keypad '+' */
    'o',       /* OPTION command              ESC-o               */
    'p',       /* GET(paste) command          ESC-p or F7         */
    'q',       /* QUIT command                ESC-q or F12        */
    'r',       /* SUBSTITUTE command          ESC-r or F6         */
    's',       /* STATUS command              ESC-s               */
    't',       /* TAGS command                ESC-t               */
    'U',       /* UPDATE command              ESC-U or F11        */
    'v',       /* VIEW command                ESC-v               */
    'w',       /* WRAP Paragraph command      ESC-w               */
    'x',       /* XECUTE command              ESC-x               */
    'z',       /* PICK command                ESC-p or Keypd "/"  */

    ',',       /* cursor left                 ESC-, or <- Key     */
    '.',       /* cursor right                ESC-. or -> Key     */
    '^',       /* cursor up                   ESC-^ or    Key     */
    '$',       /* cursor down                 ESC-$ or    Key     */
    ';',       /* HOME command                ESC-; or Keypad 5   */

    ']',       /* Word Right command          ESC-[ or ESC- ->    */
    '[',       /* Word Left command           ESC-] or ESC- <-    */
    '{',       /* Paragraph Up                ESC-{               */
    '}',       /* Paragraph Down              ESC-}               */
    
    '#',       /* RUBOUT command              ESC-# or RUBOUT Key */
    
    'D',       /* DELETE CHAR command         ESC-. or Delete Key */
    'W',       /* DELETE WORD command         ESC-W or ESC-Delete */
    '<',       /* Start of line               ESC-< or HOME Key   */
    '>',       /* End of line                 ESC-> or END  Key   */
    'B',       /* Page Up                     ESC-B or PgUp Key   */
    '\\',      /* Top of File                 ESC-\ or ESC-PgUp   */
    'F',       /* Page Down                   ESC-F or PgDn Key   */
    '~',       /* Bottom of File              ESC-~ or ESC-PgDn   */

    '-',       /* Half Up                     ESC-- or SHFT up    */
    '+',       /* Half Down                   ESC-+ or SHFT down  */
    '(',       /* Half Left                   ESC-( or SHFT left  */
    ')',       /* Half Right                  ESC-) or SHFT right */

    'C',       /* Copy Line Del Buffer        ESC-C or CTL-c      */
    'X',       /* Cut Line Del Buffer         ESC-X or CTL-x      */
    'V',       /* Paste Line Del Buffer       ESC-V or CTL-v      */

    'H',       /* Cursor TopOfPage            ESC-H               */
    'M',       /* Cursor MiddleOfPage         ESC-M               */
    'L',       /* Cursor BottomOfPage         ESC-L               */

    '`',       /* JumpToTag Command           ESC-`               */
    'k',       /* DELETE RIGHT Command        ESC-k or F10        */
    ':',       /* Edge Command                ESC-:               */
    'c',       /* Column command              ESC-c or F3         */
    'u',       /* UNDO command                ESC-u               */

    '_',       /* Mouse command               ESC-_               */
    'A'   };   /* About VE                    ESC-A               */

/* .page */

/***********************
 * PRIVATE VARIABLES   *
 ***********************/

/* Structures and vars for mapping of extended keyboard ESC sequences, such */
/* are generated when you select number pad keys such as "/" or "*", into   *
/* the 0x100 range keys returned by the Function and arrow keys             */
 
struct str_key_entry {
   short  key_received;     /* was int  */
   short  complete;         /* was BOOL */
   short  key_to_return;    /* was int  */
   short  keys_to_eat;      /* was int  */
   short  next_key_state;   /* was int  */
};

struct str_expected_keys_this_state {
   struct str_key_entry key_entries[MAX_KEYS_PER_STATE];
};

struct str_esc_seq_table {
   struct str_expected_keys_this_state expected_keys[NUM_ESC_SEQ_STATES];
};

/* Initialization of ESC sequence key table */

/* There is a DREADFUL KLUDGE in the KS_ESC_5B_31 table, related to the 3B  */
/* entry. Pressing SHIFT+CursorRight produces ESC 5B 31 3B 32 43. This      */
/* and the other logically related SHIFT+cursor-direction keys are the ONLY */
/* ones that can't be fully resolved in 4 tables. Rather than add two more  */
/* tables for just these four entries, they are directly resolved in the    */
/* code below, triggered by the key to return being RESOLVE_LAST_KEY. In    */
/* this case, the real key value to return is determined by the last of the */
/* two keys the ESC 5B 31 3B entry says to "eat". 43=SHFT+CursorRight,      */
/* 44=SHFT+CursorLeft, 41=SHFT+CursorUp and 42=SHFT+CursorDown.             */

struct str_esc_seq_table esc_seq_table = {

   /* ESC table - valid keys after ESC is received */

   0x4f,  FALSE, 0,                        0, KS_ESC_4F,
   0x5b,  FALSE, 0,                        0, KS_ESC_5B,
   0x1b,  FALSE, 0,                        0, KS_ESC,
   0,     0,     0,                        0, KS_COMPLETE,
   0,     0,     0,                        0, KS_COMPLETE,
   0,     0,     0,                        0, KS_COMPLETE,
   0,     0,     0,                        0, KS_COMPLETE,
   0,     0,     0,                        0, KS_COMPLETE,
   0,     0,     0,                        0, KS_COMPLETE,
   0,     0,     0,                        0, KS_COMPLETE,
   0,     0,     0,                        0, KS_COMPLETE,
   0,     0,     0,                        0, KS_COMPLETE,
   0,     0,     0,                        0, KS_COMPLETE,
   0,     0,     0,                        0, KS_COMPLETE,

   /* ESC 4F table - valid keys after ESC 4F is received */

   0x45,  TRUE,  LK_KEYPAD_5,              0, KS_COMPLETE,
   0x46,  TRUE,  LK_KEYPAD_END,            0, KS_COMPLETE,
   0x48,  TRUE,  LK_KEYPAD_HOME,           0, KS_COMPLETE,
   0x4d,  TRUE,  ENTER,                    0, KS_COMPLETE,
   0x50,  TRUE,  LK_F1,                    0, KS_COMPLETE,
   0x51,  TRUE,  LK_F2,                    0, KS_COMPLETE,
   0x52,  TRUE,  LK_F3,                    0, KS_COMPLETE,
   0x53,  TRUE,  LK_F4,                    0, KS_COMPLETE,
   0x6a,  TRUE,  LK_KEYPAD_STAR,           0, KS_COMPLETE,
   0x6b,  TRUE,  LK_KEYPAD_PLUS,           0, KS_COMPLETE,
   0x6d,  TRUE,  LK_KEYPAD_MINUS,          0, KS_COMPLETE,
   0x6f,  TRUE,  LK_KEYPAD_SLASH,          0, KS_COMPLETE,
   0x157, TRUE,  ENTER,                    0, KS_COMPLETE,
   0,     0,     0,                        0, KS_COMPLETE,

   /* ESC 5B table - valid keys after ESC 5B is received */

   0x31,  FALSE, 0,                        0, KS_ESC_5B_31,
   0x32,  FALSE, 0,                        0, KS_ESC_5B_32,
   0x33,  TRUE,  LK_KEYPAD_DEL,            1, KS_COMPLETE,

   /* The following two values ESC [ 5 and ESC [ 6 are        */
   /* usually associated with the Page Up and Page Down       */
   /* keys. For some reason, Mac OS X does an ESC timeout     */
   /* for all "ESC [ n" sequences where "n" is between 1 & 6  */
   /* Hence, for the Mac OS X port of VE, these are replaced  */
   /* with ESC [ 7 and ESC [ 8 respectively.                  */

/* 0x35,  TRUE,  LK_KEYPAD_PGUP,           1, KS_COMPLETE,    */
/* 0x36,  TRUE,  LK_KEYPAD_PGDN,           1, KS_COMPLETE,    */

   0x37,  TRUE,  LK_KEYPAD_PGUP,           0, KS_COMPLETE,
   0x38,  TRUE,  LK_KEYPAD_PGDN,           0, KS_COMPLETE,

   /* We now return to our regular programming! Pun Intended! */

   0x41,  TRUE,  LK_KEYPAD_CURSOR_UP,      0, KS_COMPLETE,
   0x42,  TRUE,  LK_KEYPAD_CURSOR_DOWN,    0, KS_COMPLETE,
   0x43,  TRUE,  LK_KEYPAD_CURSOR_RIGHT,   0, KS_COMPLETE,
   0x44,  TRUE,  LK_KEYPAD_CURSOR_LEFT,    0, KS_COMPLETE,
   0x45,  TRUE,  LK_KEYPAD_5,              0, KS_COMPLETE,
   0x46,  TRUE,  LK_KEYPAD_END,            0, KS_COMPLETE,
   0x48,  TRUE,  LK_KEYPAD_HOME,           0, KS_COMPLETE,
   0x50,  TRUE,  LK_KEYPAD_PAUSE,          0, KS_COMPLETE,
   0x5b,  FALSE, 0,                        0, KS_ESC_5B_5B,

   /* ESC 5B 31 table - valid keys after ESC 5B 31 is received */

   0x35,  TRUE,  LK_F5,                    1, KS_COMPLETE,
   0x37,  TRUE,  LK_F6,                    1, KS_COMPLETE, 
   0x38,  TRUE,  LK_F7,                    1, KS_COMPLETE,
   0x39,  TRUE,  LK_F8,                    1, KS_COMPLETE,
   0x3b,  TRUE,  RESOLVE_LAST_KEY,         2, KS_COMPLETE,
   0,     0,     0,                        0, KS_COMPLETE,
   0,     0,     0,                        0, KS_COMPLETE,
   0,     0,     0,                        0, KS_COMPLETE,
   0,     0,     0,                        0, KS_COMPLETE,
   0,     0,     0,                        0, KS_COMPLETE,
   0,     0,     0,                        0, KS_COMPLETE,
   0,     0,     0,                        0, KS_COMPLETE,
   0,     0,     0,                        0, KS_COMPLETE,
   0,     0,     0,                        0, KS_COMPLETE,

   /* ESC 5B 32 table - valid keys after ESC 5B 32 is received */

   0x30,  TRUE,  LK_F9,                    1, KS_COMPLETE,
   0x31,  TRUE,  LK_F10,                   1, KS_COMPLETE, 
   0x33,  TRUE,  LK_F11,                   1, KS_COMPLETE,
   0x34,  TRUE,  LK_F12,                   1, KS_COMPLETE,
   0x7e,  TRUE,  LK_KEYPAD_INS,            0, KS_COMPLETE,
   0,     0,     0,                        0, KS_COMPLETE,
   0,     0,     0,                        0, KS_COMPLETE,
   0,     0,     0,                        0, KS_COMPLETE,
   0,     0,     0,                        0, KS_COMPLETE,
   0,     0,     0,                        0, KS_COMPLETE,
   0,     0,     0,                        0, KS_COMPLETE,
   0,     0,     0,                        0, KS_COMPLETE,
   0,     0,     0,                        0, KS_COMPLETE,
   0,     0,     0,                        0, KS_COMPLETE,

   /* ESC 5B 5B table - valid keys after ESC 5B 5B is received */

   0x41,  TRUE,  LK_F1,                    0, KS_COMPLETE,
   0x42,  TRUE,  LK_F2,                    0, KS_COMPLETE, 
   0x43,  TRUE,  LK_F3,                    0, KS_COMPLETE,
   0x44,  TRUE,  LK_F4,                    0, KS_COMPLETE,
   0x45,  TRUE,  LK_F5,                    0, KS_COMPLETE,
   0,     0,     0,                        0, KS_COMPLETE,
   0,     0,     0,                        0, KS_COMPLETE,
   0,     0,     0,                        0, KS_COMPLETE,
   0,     0,     0,                        0, KS_COMPLETE,
   0,     0,     0,                        0, KS_COMPLETE,
   0,     0,     0,                        0, KS_COMPLETE,
   0,     0,     0,                        0, KS_COMPLETE,
   0,     0,     0,                        0, KS_COMPLETE,
   0,     0,     0,                        0, KS_COMPLETE

};

int unexpected_keys  = 0; /* Error keys received            */
int key_table_errors = 0; /* State errors in the key tables */

/* .page */

/**********************
 * PRIVATE PROCEDURES *
 **********************/

int find_key ( key_state, key )

/****************************************************************************
 *                                                                          *
 * This function accepts a key state and a key, and attempts to find the    *
 * key in the key table indexed by key_state. If found, its index is        *
 * returned. Else -1 is returned.                                           *
 *                                                                          *
 ****************************************************************************/

int key_state;   /* Index to the key table to search                        */
int key;         /* The key to look for                                     */

begin
   int key_ix = -1;
   register int i;

   struct str_expected_keys_this_state *expected_keys;

   expected_keys = &esc_seq_table.expected_keys[key_state];

   for ( i = 0; i < MAX_KEYS_PER_STATE; i++ )
      if ( expected_keys->key_entries[i].key_received == key )
      begin
         key_ix = i;
         break;
      end

   return ( key_ix );

end /* find_key */

/* .page */

/*********************
 * PUBLIC PROCEDURES *
 *********************/

int read_raw_char ()

/****************************************************************************
 *                                                                          *
 *  Read and return one character.  This character is either read from the  *
 *  operating system, or from the current macro. The returned key may be    *
 *  larger than one byte, such as a 0x100 series extended keyboard key.     *
 *                                                                          *
 ****************************************************************************/

 begin
    int key;    /* returned key value                                       */

    /* Check for an init macro. Keys loaded here will be used by VE ahead   */
    /* of new keys read from the keyboard. The init macro is used by main   */
    /* to implement the "-b" (browse) option of VE. In future, this macro   */
    /* may be used by other parts of VE to essentially ungetch keys into    */
    /* the command stream.                                                  */

    if ( init_macro_keys > 0 )
    begin
       key = init_macro[init_macro_ix];

       init_macro_keys -= 1;
       init_macro_ix   += 1;

       return ( key );
    end

    refresh (); /* update the screen before waiting for input               */

    switch ( read_state ) begin

        case RS_TRANSPARENT:

            /* State machine is in transparent mode.  Pass right through    */
            /* to the Operating System.                                     */

            key = getch ();
            break;

        case RS_LEARNING:

            /* State machine is in the learning state.  This is the same    */
            /* as the transparent case, except that all keystrokes are      */
            /* remembered in the current macro buffer.                      */

            key = getch();

            if ( curr_macro->length < MAX_MACRO_SIZE - 1 ) then
                 curr_macro->keys[curr_macro->length++] = key;

            break;

        case RS_MACRO:

            /* State machine is reading from a macro at this time.  If the  */
            /* macro is not exhausted, take the next key.  If the macro is  */
            /* exhausted, and there are more iterations of the macro left,  */
            /* reset it, and start again.  Else, go to OS for char.         */

            if ( rs_ix != curr_macro->length ) then
                 key = curr_macro->keys[rs_ix++];

            else begin

                /* Current iteration of macro is exhausted */

                if ( rs_reps_left > 0 ) begin

                    /* Macro iterations not exhausted */

                    rs_reps_left -= 1;
                    rs_ix         = 0;

                    key = curr_macro->keys[rs_ix++];
                end

                else begin

                    /* Macro is totally over.  Return to RS_TRANSPARENT     */
                    /* and close off the undo action for the macro now      */
                    /* finishing.                                           */

                    read_state = RS_TRANSPARENT;
                    curr_macro = NULL;

                    complete_push_undo_macro ();
                    set_status_line ( TRUE, E_OK, last_ve_mode );

                    key = getch ();
                end

            end /* Current iteration over */

            break;


        default:
            /* Read_state is out of bounds !! Yikes !! */

            write_ve_msg ( "Read_Char: Invalid Read_state" );

            /* Repair the damage, and attempt to carry on */
            
            read_state = RS_TRANSPARENT;
            curr_macro = NULL;

            break;

    end /* switch */

    return ( key );
    
end /* read_raw_char */

/* .page */

int map_ctl_keys ( command_key )

/****************************************************************************
 *                                                                          *
 *  This functions accepts a command_key that has resulted from a key press *
 *  like CTL-x. It maps these keypresses into their intended command chars, *
 *  effectively creating keyboard shortcuts to typing "ESC char_cmd" for    *
 *  the keys its supports.                                                  *
 *                                                                          *
 *  THIS COMMAND MAPS CONTROL KEYS (CTL-A - CTL-Z) TO THEIR ASSOCIATED      *
 *  COMMANDS!                                                               *
 *                                                                          *
 ****************************************************************************/

 int command_key; /* the key read from the OS */

 begin
    register int cmd_ix = 0x77;  /* Index of the command to return          */
    int key;                     /* Command key to return                   */

    switch ( command_key ) begin

       /* Cursor, Home, Middle, Bottom */

       case CTL_T:
          cmd_ix = top_of_page_ix;
          break;
       case CTL_G:
          cmd_ix = middle_of_page_ix;
          break;
       case CTL_B:
          cmd_ix = bottom_of_page_ix;
          break;
       
       /* Cursor Up, Down, Left, Right */

       case CTL_U:
          cmd_ix = cursor_up_ix;
          break;
       case CTL_D:
          cmd_ix = cursor_down_ix;
          break;
       case CTL_L:
          cmd_ix = cursor_left_ix;
          break;
       case CTL_R:
          cmd_ix = cursor_right_ix;
          break;

       /* Next Page, Previous Page */

       case CTL_N:
          cmd_ix = ext_pgdn_ix;
          break;
       case CTL_P:
          cmd_ix = ext_pgup_ix;
          break;
    
       /* Line Copy, Cut and Paste */

       case CTL_C:
          cmd_ix = copy_line_del_ix;
          break;
       case CTL_X:
          cmd_ix = cut_line_del_ix;
          break;
       case CTL_V:
          cmd_ix = paste_line_del_ix;
          break;

       /* Map ^E to Extended Motion Command - original Qunix HOME command   */

       case CTL_E:
          cmd_ix = extended_motion_ix;
          break;

       /* Handle ^H as backspace */

       case CTL_H:
          cmd_ix = rubout_ix;
          break;

       /* Map ^Z to Undo */

       case CTL_Z:
          cmd_ix = undo_ix;
          break;

       /* Map ^O to Insert/Overwrite - Usefull on Macs with no Insert Key! */

       case CTL_O:
          cmd_ix = insert_ix;
          break;

       /* Special case - TAB (0x09) - just return it */

       case '\t':
          key = '\t';
          break;

       /* Special case - ENTER (0x0a)- just return it */

       case ENTER:
          key = ENTER;
          break;

       /* Unimplemented CTL code. Map it to INSERT - harmless - and beep    */

       default:
          cmd_ix = insert_ix;
          beep ();
          break;

   end /* switch */

   if ( cmd_ix != 0x77 ) begin
      key = commands[cmd_ix];
      ve_mode = M_COMMAND;
   end

   return ( key );

end /* map_ctl_keys */

/* .page */

int map_extended_keyboard_keys ( command_key )

/****************************************************************************
 *                                                                          *
 *  This function accepts a command_key that has resulted from a key press  *
 *  of one of the PC extended keyboard keys, such as F1-F12, or keypad      *
 *  keys like Insert, Del, Home, End, PgUp, PgDn, arrow left, arrow right,  *
 *  arrow up, arrow down, or the keypad "/", "*", "-" or "+" keys and maps  *
 *  these to their intended command chars, effectively creating keyboard    *
 *  shortcuts to typing "ESC char_cmd" for the keys it supports.            *
 *                                                                          *
 *  THIS COMMAND MAPS THE FUNCTION KEYS (F1-F12) TO THEIR ASSOCIATED        *
 *  COMMANDS!                                                               *
 *                                                                          *
 ****************************************************************************/

 int command_key; /* the key read from the OS */

 begin
    register int cmd_ix;        /* Index of the command to return           */
    int key;                    /* The key to return                        */
    ext_cmd = TRUE;             /* Have received an extended keyboard key   */

    switch ( command_key ) begin

       case LK_F1: 
          cmd_ix = again_ix;
          break;

       case LK_F2: 
          cmd_ix = block_ix;
          break;

       case LK_F3: 
          cmd_ix = column_ix;
          break;

       case LK_F4: 
          cmd_ix = jump_ix;
          break;

       case LK_F5: 
          cmd_ix = find_ix;
          break;

       case LK_F6: 
          cmd_ix = substitute_ix;
          break;

       case LK_F7: 
          cmd_ix = get_ix;
          break;

       case LK_F8: 
          cmd_ix = edit_ix;
          break;

       case LK_F9: 
          cmd_ix = del_line_ix;
          break;

       case LK_F10: 
          cmd_ix = del_right_ix;
          break;

       case LK_F11: 
          cmd_ix = update_ix;
          break;

       case LK_F12: 
          cmd_ix = quit_ix;
          break;

       case LK_BACKSPACE:
          cmd_ix = rubout_ix;
          break;

       case LK_KEYPAD_INS:
          cmd_ix = insert_ix;
          break;

       case LK_KEYPAD_DEL:
          /* If we are already in command mode when we receive Delete, the  */
          /* user has entered ESC Delete or ALT Delete. Map this to "delete */
          /* word".                                                         */

          if ( ve_mode == M_COMMAND )
             cmd_ix = del_word_ix;
          else
             cmd_ix = del_char_ix;
          break;

       case LK_KEYPAD_HOME:
       case LK_KEYPAD_HOME_2:
          cmd_ix = ext_home_ix;
          break;

       case LK_KEYPAD_END:
       case LK_KEYPAD_END_2:
          cmd_ix = ext_end_ix;
          break;

       case LK_KEYPAD_PGUP:
          /* If we are already in command mode when we receive Page Up,     */
          /* the user has entered ESC Page Up or ALT Page Up. Map this to   */
          /* "move to top of file".                                         */

          if ( ve_mode == M_COMMAND )
             cmd_ix = ext_top_of_file_ix;
          else
             cmd_ix = ext_pgup_ix;
          break;

       case LK_KEYPAD_PGDN:
          /* If we are already in command mode when we receive Page Down,   */
          /* the user has entered ESC Page Down or ALT Page Down. Map this  */
          /* to "move to bottom of file".                                   */

          if ( ve_mode == M_COMMAND )
             cmd_ix = ext_end_of_file_ix;
          else
             cmd_ix = ext_pgdn_ix;
          break;

       case LK_KEYPAD_CURSOR_LEFT:
          /* If we are already in command mode when we receive cursor left, */
          /* the user has entered ESC cursor_left or ALT cursor_left. Map   */
          /* this to word_left.                                             */

          if ( ve_mode == M_COMMAND )
             cmd_ix = word_left_ix;
          else
             cmd_ix = cursor_left_ix;
          break;

       case LK_KEYPAD_CURSOR_RIGHT:
          /* If we are already in command mode when we receive cursor right, */
          /* the user has entered ESC cursor_right or ALT cursor_right. Map  */
          /* this to word_right.                                             */

          if ( ve_mode == M_COMMAND )
             cmd_ix = word_right_ix;
          else
             cmd_ix = cursor_right_ix;
          break;

       case LK_KEYPAD_CURSOR_UP:
          if ( ve_mode == M_COMMAND )
             cmd_ix = para_up_ix;
          else
             cmd_ix = cursor_up_ix;
          break;

       case LK_KEYPAD_CURSOR_DOWN:
          if ( ve_mode == M_COMMAND )
             cmd_ix = para_down_ix;
          else
             cmd_ix = cursor_down_ix;
          break;

       case LK_SHFT_CUR_LEFT:
          cmd_ix = half_left_ix;
          break;

       case LK_SHFT_CUR_RIGHT:
          cmd_ix = half_right_ix;
          break;

       case LK_SHFT_CUR_UP:
          cmd_ix = half_up_ix;
          break;

       case LK_SHFT_CUR_DOWN:
          cmd_ix = half_down_ix;
          break;

       case LK_KEYPAD_SLASH:
          cmd_ix = pick_ix;
          break;

       case LK_KEYPAD_STAR:
          cmd_ix = info_ix;
          break;

       case LK_KEYPAD_MINUS:
          cmd_ix = goto_file_ix;
          break;

       case LK_KEYPAD_PLUS:
          cmd_ix = next_file_ix;
          break;

       case LK_KEYPAD_5:
          cmd_ix = edge_ix;
          break;

       case KEY_MOUSE:
          /* Received a mouse event - have the mouse_cmd called */

          cmd_ix = mouse_ix;
          break;

       default:
          /* Uh oh - this shouldn't happen. If it does, return the Insert  */
          /* command - this is completely harmless, just placing VE into   */
          /* insert mode. It makes no file content changes.                */

          writechar ( BEL);
          cmd_ix = insert_ix;
          break;

    end /* switch */

    key     = commands [cmd_ix];    
    ve_mode = M_COMMAND;

    return ( key );

end /* map_extended_keyboard_keys */

/* .page */

int unmap_extended_keyboard_key ( cmd_ix )

/***************************************************************************
 *                                                                         *
 * This function performs a partial unmap of an extended keyboard key. The *
 * function map_extended_keyboard_keys maps an extended keyboard key into  *
 * a normal VE command composed of ESC, command_letter. It also detects    *
 * situations where the user has either previously pressed ESC, or has     *
 * pressed ALT while pressing the extended keyboard key. In this case, for *
 * some extended keyboard keys, it returns a different command key. For    *
 * example, pressing Delete causes it to generate the equivalent of Delete *
 * Char. Pressing ALT-Delete causes it to generate the equivalent of       *
 * Delete Word. 
 *                                                                         *
 * Why all this preamble? Well, all of this mapping causes problems when   *
 * the user enters something like "ESC 5 Delete". What did they mean?      *
 * Did they mean "delete 5 chars" or "delete 5 words"? This is where the   *
 * VE keyboard mapping model has outsmarted itself into a bit of a box.    *
 *                                                                         *
 * Since they typed ESC ahead of the repetition count (they have to in     *
 * order to enter that count), the map_extended_keyboard_keys function     *
 * will map it to "delete 5 words", making the decision for us. However,   *
 * the author of VE really wants this sequence to mean "delete 5 chars".   *
 *                                                                         *
 * Thats where this little function comes in. It is called by the command  *
 * processor in modeproc.c when it realizes that an extended keyboard key  *
 * has been entered AND the user has just entered a repetition count. In   *
 * this case, the extended keyboard key has already been mapped to the     *
 * "wrong" value for design intent. This function undoes that, partially   *
 * unmapping the extended keyboard key back to its un-ALT-ered meaning.    * 
 *                                                                         *
 * One last wrinkle... for real time efficiency, this function is supplied *
 * with and operates on the indexes of the commands, rather than the       *
 * command keys themselves - saves all that indexing. It does return an    *
 * actual command key when it is done though.                              *
 *                                                                         *
 * So... This function examines the command index that has been supplied.  *
 * If the command is one of those that is the result of a key press that   *
 * returns one value if ESC has been previously pressed and another if it  *
 * hasn't, this function returns the "it hasn't" value of the key. Else it *
 * simply returns the key that was supplied.                               *
 *                                                                         *
 * Phew! Think that was too much to comprehend? Just trying dreaming this  *
 * stuff up!! ANY function that needs this much documentation is WAY too   *
 * convoluted!                                                             *
 *                                                                         *
 ***************************************************************************/

int *cmd_ix;               /* The index of the command to be "unmapped"    */

begin
   int unmapped_cmd_ix;    /* The return result                            */

   switch ( *cmd_ix ) begin

      case word_right_ix:
         unmapped_cmd_ix = cursor_right_ix;
         break;

      case word_left_ix:
         unmapped_cmd_ix = cursor_left_ix;
         break;

      case para_up_ix:
         unmapped_cmd_ix = cursor_up_ix;
         break;

      case para_down_ix:
         unmapped_cmd_ix = cursor_down_ix;
         break;

      case del_word_ix:
         unmapped_cmd_ix = del_char_ix;
         break;

      case ext_top_of_file_ix:
         unmapped_cmd_ix = ext_pgup_ix;
         break;

      case ext_end_of_file_ix:
         unmapped_cmd_ix = ext_pgdn_ix;
         break;

      default:
         unmapped_cmd_ix = *cmd_ix;
         break;

   end /* switch *cmd_ix */

   *cmd_ix = unmapped_cmd_ix;
   return ( commands[unmapped_cmd_ix] );

   /* Simple, n'est pas? :-)  */

end /* unmap_extended_keyboard_key */

/* .page */

int handle_esc_sequence ()

/****************************************************************************
 *                                                                          *
 * This function is called when an ESC is read from the keyboard. If the    *
 * next key is 4f or 5b, it is part of an extended keyboard ESC sequence,   *
 * which this function handles and maps to its 0x100 range key code. Else,  *
 * the next key is returned as expected.                                    *
 *                                                                          *
 ****************************************************************************/

begin
   int key, key_ix;             /* The key read and its index in key table  */
   register int i, throw_away;  /* Loop var, key to throw away              */
   BOOL done = FALSE;           /* Is handling of sequence complete?        */
   int key_state = KS_ESC;      /* The current key state - index into the   */
                                /* key tables                               */

   struct str_key_entry *key_entry;
   struct str_expected_keys_this_state *expected_keys;

   do {

      key = read_raw_char ();
   
      expected_keys = &esc_seq_table.expected_keys[key_state];

      /* Check to see if this is one of the expected keys in this keystate  */

      key_ix = find_key ( key_state, key );

      if ( key_ix == -1 ) 
      begin
         /* This is not an expected key for this key state. Either this is  */
         /* not part of an extended sequence, or it is an error. Either way */
         /* set VE into command mode, so that we will attempt to interpret  */
         /* it as a command.                                                */

         done    = TRUE;
         ve_mode = M_COMMAND;

         if ( key_state != KS_ESC )
         begin
            /* We are beyond the initial state, so this is not an ESC-x     */
            /* command. If we have a key we don't recognize, this is an     */
            /* an error. Peg an unexpected key counter and return something */
            /* harmless. This function uses the insert command.             */

            unexpected_keys += 1;

            key = commands[insert_ix];
         end

      end /* not an expected key */

      else
      begin
         /* This is one of the expected keys for this state. If this key    */
         /* completes the sequence, map the sequence to the expected value  */
         /* else simply advance to the next state.                          */

         if ( key == ESC )
         begin
            /* Handle the case of ESC ESC. This is the case where the user  */
            /* has typed the ESC key, and then typed an extended keyboard   */
            /* such as PgUP, which has generated an ESC sequence. Handle    */
            /* this by setting VE's mode to M_COMMAND, so that the function */
            /* that maps extended keyboard keys knows to treat it           */
            /* differently.                                                 */

            ve_mode = M_COMMAND;
         end

         key_entry = &expected_keys->key_entries[key_ix];

         if ( key_entry->complete )
         begin
            /* This key completes the sequence. Map the key to the correct  */
            /* return value, set done to true, and eat any residual keys    */

            key  = key_entry->key_to_return;
            done = TRUE;

            if ( key_entry->keys_to_eat > 0 )
               for ( i = 0; i < key_entry->keys_to_eat; i++ )
                  throw_away = read_raw_char ();

            if ( key == RESOLVE_LAST_KEY )
            begin
               /* There is a DREADFUL KLUDGE in the KS_ESC_5B_31 table,     */
               /* related to the 3B entry. Pressing SHIFT+CursorRight       */
               /* produces ESC 5B 31 3B 32 43. This and the other logically */
               /* related SHIFT+cursor-direction keys are the ONLY ones     */
               /* that can't be fully resolved in 4 tables. Rather than add */
               /* two more tables for just these four entries, they are     */
               /* directly resolved here, triggered by the key to return    */
               /* being RESOLVE_LAST_KEY. In this case, the real key value  */
               /* to return is determined by the last of the two keys the   */
               /* ESC 5B 31 3B entry says to "eat". 43=SHFT+CursorRight,    */
               /* 44=SHFT+CursorLeft, 41=SHFT+CursorUp and                  */
               /* 42=SHFT+CursorDown.                                       */

               if ( throw_away == 0x41 )
                  key = LK_SHFT_CUR_UP;
               else if ( throw_away == 0x42 )
                  key = LK_SHFT_CUR_DOWN;
               else if ( throw_away == 0x43 )
                  key = LK_SHFT_CUR_RIGHT ;
               else if ( throw_away == 0x44 )
                  key = LK_SHFT_CUR_LEFT;
               else
                  key = commands[insert_ix];     /* Unknown error - be safe */
            end
         end

         else
         begin
            /* The sequence is not complete - advance to the next state */

            if ( key_entry->next_key_state != KS_COMPLETE )
               key_state = key_entry->next_key_state;
            
            else
            begin
               /* Uh oh, serious error. The "complete" flag says we are not  */
               /* done, but the next state for this key doesn't agree. The   */
               /* key tables are b0rked! Oh deary, deary me. What to do?     */
               /* Well, lets peg another "invalid" counter, and return       */
               /* something harmless ('i' again).                            */

               key_table_errors += 1;

               key  = 'i';
               done = TRUE;
            end

         end /* sequence is not complete */

      end /* this is an expected key */

   } while ( !done );

   return ( key );

end /* handle_esc_sequence */

/* .page */

int map_keyboard ( OS_key )

/***************************************************************************
 *                                                                         *
 * This function reads a key directly from the OS, and maps special cases  *
 * as needed by VE. The mapped result is returned.                         *
 *                                                                         *
 ***************************************************************************/

int OS_key;     /* The "raw" key value, read in from the OS                */
                /* Use int, not char, since Linux will return 0x1xx codes  */
                /* for PF keys and special keys like PgUp, PgDn, etc.      */
begin
   int key;     /* The mapped output value                                 */

   /* Start by handling ESC sequences that may have come from extended     */
   /* keyboard keys                                                        */

   if ( OS_key == ESC ) 
      OS_key = handle_esc_sequence ();

   /* Map control-key to ESC-command sequence */

   if ( OS_key >= 0x01 && OS_key <= 0x1a ) 
       key = map_ctl_keys ( OS_key);

   /* Map Rubout to BACKSPACE */

   else if ( OS_key == 0x7f ) 
   begin
       ve_mode = M_COMMAND;
       key = commands[rubout_ix];
   end

   /* Map extended PC keyboard keys to ESC-command sequence */

   else if ( OS_key >= 0x100 )
       key = map_extended_keyboard_keys ( OS_key );

   /* Else it is a regular key, just return it */

   else
       key = (char) OS_key;

   return ( key );

end /* map_keyboard */

/* .page */

int read_char ()

/****************************************************************************
 *                                                                          *
 * This function reads a keyboard key and maps any extended keyboard keys,  *
 * CTL keys or ESC sequences. This function may knock VE into command mode  *
 * if the key read is any of the above.                                     *
 *                                                                          *
 ****************************************************************************/

begin
   int key;

   key = read_raw_char ();

   /* Map extended keyboard keys, ESC sequences and CTL keys          */

   key = map_keyboard ( key );

   /* Catch any and all non printing keys and set VE into command     */
   /* so that they don't end up getting inserted into the user's file */

   if (key < (char) 0x20 || key > (char) 0x7e) then
       if (key != (char) ENTER && key != (char) 0x09) then
           ve_mode = M_COMMAND;

   return ( key & 0xff );

end /* read_char */

/* .page */

int line_edit ( line_buf, buf_size, key, ix, x, y )

/****************************************************************************
 *                                                                          *
 *  This function is an asynchronous line input editor. It provides a       *
 *  full string entry and editing function, in a non blocking, one char at  *
 *  a time fashion. The caller provides a buffer to to use, the current     *
 *  index within that buffer, and the x,y co-ordinates to echo at.          *
 *                                                                          *
 *  On each invocation, the caller provides a new key that has been read    *
 *  and this function processes it into the supplied buffer providing       *
 *  full support for the expected range of line editing functions, such as  *
 *  HOME, END, Cursor Left, Cursor Right, Del, Rubout, etc.                 *
 *                                                                          * 
 *  This function treats ENTER as an effective null. It does nothing with   *
 *  it, since the function calling this routine should have declared line   *
 *  end when it saw ENTER, and done something with the input.               *
 *                                                                          *
 *  This function returns the length of the line after the current key      *
 *  is processed. The returned line buffer is ALWAYS a string (null         *
 *  terminated).                                                            *
 *                                                                          *
 ****************************************************************************/

 char line_buf [];        /* String to place input in                       */
 int  buf_size;           /* Length of buffer                               */
 int  key;                /* New key to process                             */
 int *ix;                 /* Current index into line buffer                 */
 int *x, *y;              /* Cursor position for echoing at                 */

 begin
    register int length;  /* Length of string so far                        */
    register int i;       /* Loop counter                                   */

    length = strlen(line_buf);

    if ( ve_mode == M_COMMAND ) 
    begin
       /* Command key entered. It may be one of HOME, END, CUR LEFT,        */
       /* CUR RIGHT, DELETE or Backspace/Rubout                             */

       if ( key == commands[rubout_ix] ) 
       begin
          /* RUBOUT received */

          if ( *ix > 0 ) 
          begin
             if ( read_state != RS_MACRO )
             begin
                ti_move_cursor ( *x - 1, *y );
                ti_delete_char();
             end

             *ix -= 1;
             *x  -= 1;

             for ( i = *ix; i < length; i++ )
                line_buf[i] = line_buf[i+1];

             length -= 1;
          end
          else
              writechar ( BEL );

      end /* RUBOUT */

      else if ( key == commands[del_char_ix] ) 
      begin
          /* DELETE CHAR received */

          if ( *ix >= 0 ) 
          begin
             if ( read_state != RS_MACRO )
             begin
                ti_move_cursor ( *x, *y );
                ti_delete_char ();
             end

             for ( i = *ix; i < length; i++ )
                line_buf[i] = line_buf[i+1];

             length = (length > 0) ? length-- : 0;
          end

          else
              writechar ( BEL );

      end /* DELETE CHAR */

      else if ( key == commands[del_right_ix] )
      begin
         /* DELETE RIGHT received - clear to end of buffer */

         if ( read_state != RS_MACRO )
         begin
            ti_move_cursor ( *x, *y );
            for ( i = *ix; i < length; i++ )
               ti_delete_char ();
         end

         length = *ix;

      end /* DELETE RIGHT */

      else if ( key == commands[del_left_ix] )
      begin
         /* DELETE LEFT received - clear to start of buffer */

         if ( read_state != RS_MACRO )
         begin
            ti_move_cursor ( (*x = *x - *ix), *y ); /* Cursor to start of buffer */
            for ( i = 0; i < *ix; i++ )
               ti_delete_char ();
         end

         for ( i = 0; i <= *ix; i++ )
            line_buf[i] = line_buf[i + *ix];

         length = (length >= *ix) ? length - *ix : 0;
         *ix    = 0;

      end /* DELETE LEFT */

      else if ( key == commands[ext_home_ix] )
      begin
         /* HOME received - move to start of buffer */

         if ( read_state != RS_MACRO )
              ti_move_cursor ( (*x = *x - *ix), *y );

         *ix = 0;

      end /* HOME */

      else if ( key == commands[ext_end_ix] )
      begin
         /* END received - move to end of received input */

         if ( read_state != RS_MACRO )
              ti_move_cursor ( (*x = *x - *ix + length), *y );

         *ix = length;

      end /* END */

      else if ( key == commands[cursor_left_ix] )
      begin
         /* Cursor Left received - move left one position */

         if ( *ix > 0 ) 
         begin
            *ix -= 1;
            *x  -= 1;

            if ( read_state != RS_MACRO )
                 ti_move_cursor ( *x, *y );
         end
         else
            writechar ( BEL );

      end /* Cursor Left */

      else if ( key == commands[cursor_right_ix] )
      begin
         /* Cursor Right received - move right one position */

         if ( *ix < buf_size && *ix < length )
         begin
             *ix += 1;
             *x  += 1;

             if ( read_state != RS_MACRO )
                  ti_move_cursor ( *x, *y );
         end
         else
            writechar ( BEL );

      end /* Cursor Right */

      else
         writechar ( BEL );

      ve_mode = last_ve_mode; /* get VE back out of command mode */

    end /* Command mode - HOME, END, CUR LEFT, CUR RIGHT, BKSPC, RUB entered */

    else if ( key >= 0x20 && key <= 0x7e )
    begin
        /* If this is a normal key, echo and buffer it */

        if ( *ix < buf_size - 1 )
        begin
           if ( read_state != RS_MACRO )
           begin
               ti_move_cursor ( (*x)++, *y );
               ti_insert_char ( key );
           end

           for ( i = ++length; i > *ix; i-- )
              line_buf[i] = line_buf[i-1];

           line_buf[(*ix)++] = key;
        end
        else
            writechar ( BEL );
    end

    line_buf[length] = '\0';
    return ( strlen(line_buf) );

end /* line_edit */

/* .page */

int read_str ( str, str_size, x, y, clear, preloaded )

/****************************************************************************
 *                                                                          *
 *  This function reads a string of input from the console, terminated by   *
 *  ENTER. The string is placed into the supplied string "str" and the      *
 *  characters received are echoed starting at (x, y).                      *
 *                                                                          *
 *  This function uses line_edit to provide the user with full line edit    *
 *  capability while entering the string.                                   *
 *                                                                          *
 *  This function returns the length of the string that was read, or 0 if   *
 *  the user simply entered a ENTER key.                                    *
 *                                                                          *
 ****************************************************************************/

 char str[];              /* String to place input in                       */
 int  str_size;           /* Length of buffer                               */
 int  x, y;               /* Cursor position for echoing at                 */
 BOOL clear;              /* Clear to end of line before echoing            */
 BOOL preloaded;          /* Is the response string preloaded?              */

begin
   int ix = 0;            /* Index into str of next character position      */
   int key;               /* Current key read                               */
   register int length;   /* Length of read string                          */

   if ( !preloaded )
      str[0] = '\0';

   if ( clear && read_state != RS_MACRO )
   begin
      /* If we should clear to the end of the line before starting, do so   */

      ti_move_cursor ( x, y );
      ti_clear_to_eol ( y );
   end

   if ( preloaded && strlen(str) > 0 && read_state != RS_MACRO )
   begin
      /* The response string has been preloaded with text. Echo this out as */
      /* if it had been typed, and then put the cursor at the start of the  */
      /* echo'd output.                                                     */

      ti_move_cursor ( x, y );
      writestr ( str );
      ti_move_cursor ( x, y );
   end

   ve_mode = last_ve_mode; /* Kick VE out of command mode in case it is in  */

   do
      length = line_edit ( str, str_size, (key = read_char()), &ix, &x, &y );

   while ( key != ENTER );

   return ( length );

end /* read_str */

/* .page */

int prompt_read_str ( px, py, pstr, ex, ey, rstr, rstr_size )

/****************************************************************************
 *                                                                          *
 *  Put out a prompt to the console, and read in a response from that       *
 *  prompt.  Returns length of string read (0 if blank line).  Args are:    *
 *                                                                          *
 *  px, py          Are x,y position to move cursor to for prompt           *
 *                  -1,-1 means no move                                     *
 *                                                                          *
 *  pstr            Prompt string                                           *
 *                                                                          *
 *  ex,ey           Are x,y position to go to for echoing response          *
 *                  -1,-1 means to echo immediately after prompt            *
 *                  -2,-2 means to preload rstr with prompt, and move       *
 *                        cursor to (px, py) before echoing                 *
 *                                                                          *
 *                  In both cases, line is cleared to end before echo       *
 *                                                                          *
 *  rstr            String to read response into                            *
 *  rstr_size       Maximum length of rstr                                  *
 *                                                                          *
 *  NOTE: HORIZONTAL SCROLLING IS DISABLED DURING THIS FUNCTION, WHICH      *
 *        MEANS THAT (px, py) AND (ex, ey) ARE INTERPRETED AS RELATIVE TO   *
 *        TO THE LEFT EDGE OF THE PHYSICAL DISPLAY.                         *
 *                                                                          *
 ****************************************************************************/

 int px, py;        /* Cursor position for prompt message                   */
 char pstr [];      /* Prompt message string                                */

 int ex, ey;        /* Cursor position for echoing response                 */
 char rstr [];      /* String to read response into                         */
 int  rstr_size;    /* Maximum length of rstr                               */

 begin
    int length = 0;

    ti_disable_horizontal_scrolling ();

    if ( px >= 0 && py >= 0 && read_state != RS_MACRO ) 
    begin
       /* Put out the prompt string */

       if ( ex != -2 && ey != -2 )
       begin
          /* If (ex, ey) are -2, the prompt string is going to be preloaded */
          /* into the response string and echo'd by the string reader. So,  */
          /* don't bother putting it out here - no sense doing it twice.    */

          ti_move_cursor ( px, py );
          ti_clear_to_eol ( py );

          writestr ( pstr );
       end
    end

    /* Logically clear the response string (preload it with null)           */

    rstr[0] = '\0';

    /* If (ex, ey) are -1, this means that we are supposed to echo after    */
    /* the prompt message. So, figure out a new ex setting based on the     */
    /* length of the prompt message, and supply it to the string reader     */

    if ( ex == -1 && ey == -1 )
    begin
       ex = strlen(pstr);
       ey = py;
    end

    /* If (ex, ey) are -2, this means that we are supposed to preload the   */
    /* response string with the prompt string and echo in the prompt space. */

    else if ( ex == -2 && ey == -2 )
    begin
       movb ( pstr, rstr, strlen(pstr) + 1 );
       ex = px;
       ey = py;
    end

    /* Now read in the response. The response string is always preloaded,   */
    /* either with the prompt string, or with null                          */

    length = read_str ( rstr, rstr_size, ex, ey, TRUE, TRUE );

    /* Restore the cursor and get outta Dodge! */

    ti_enable_horizontal_scrolling ();
    restore_cursor ();

    return ( length );

end /* prompt_read_str */

/* .page */

void wait_for_enter ()

/*****************************************************************************
 *                                                                           *
 *  This procedure is called whenever a part of VE wants to wait until the   *
 *  user presses the enter key. It simply waits for this event, beeps if any *
 *  other key is pressed, and returns when ENTER is received.                *
 *                                                                           *
 *****************************************************************************/

begin
    register char key;            /* user input keys                         */

    do 
       begin
          key = read_char ();

          if ( key != ENTER ) then
             writechar ( BEL );

       end 
    
    while ( key != ENTER );

end /* wait_for_enter */

