/*
 *  BindWKeys.c     Reads a file and creates an array of key bindings
 *                  to be used by a hot-key handler to tell which keys
 *                  do what.
 *
 *             Copyright (c) 1987 by Davide P. Cervone
 *  You may use this code provided this copyright notice is left intact.
 */

#include <exec/types.h>
#include <exec/memory.h>
#include <devices/inputevent.h>
#ifndef NO_FILE
#include <stdio.h>
#endif

#include "wKeys.h"

#ifndef NO_FILE
#define KEYMASK         0x00FF      /* default qualifier key mask */
#define SHIFT           IECODE_UP_PREFIX

#define BADVALUE        -1          /* error while parsing input line */

/*
 *  Macros to tell whether a character is printable or not, and
 *  whether it is not alphanumeric
 */
#define PRINTABLE(c)    ((c)>=' '&&(c)<='~')
#define NOTPRINTABLE(c) ((c)<' '||(c)>'~')
#define NOTALPHANUM(c)\
   ((c)<'0'||((c)>'9'&&(c)<'A')||((c)>'Z'&&(c)<'a')||(c)>'z')

/*
 *  Create a new instance of a given structure type
 */
#define NEW(s,var)      (var = (struct s *)New("var",sizeof(struct s)))


#define LINESIZE    132
static char InputLine[LINESIZE+1];    /* the line read from the file */
static char *CurPos;                  /* current character position in line */
static char *CurWord;                 /* pointer to begining of current word */
static char TerminationChar;          /* character that ended the word */
static int LineCount = 0;             /* number of lines read from the file */
#endif

struct HotKeyItem *KeyList = NULL;    /* the list of key definitions so far */
struct HotKey *KeyArray = NULL;       /* the sorted array of key definitions */
int KeyCount = 0;                     /* the number of key definitions */
#define KEYARRAYSIZE    (KeyCount*sizeof(struct HotKey))

#ifndef NO_FILE
static FILE *InFile = NULL;           /* the input file */
#define ERROR _OSERR                  /* the system's error variable */
extern int ERROR;


/*
 *  This structure maps a character string to a numeric value.  It is used
 *  for mapping key names to keyboard scan codes and key action names to 
 *  action numbers.
 */

struct Definition
{
   char *Name;
   UBYTE Code;
};


/*
 *  Qualifier[] maps the names of the qualifier keys to their corresponding 
 *  bit positions within the ie_Qualifier field of an InputEvent.  This List 
 *  is sorted by name.  Note that LCOMMAND is equivalent to LAMIGA, etc.  
 *  Note also that there are some compound qualifier names, such as SHIFT.  
 *  These are expanded into more than one key definition.
 *
 *  See devices/inputevent.h for more details on qualifier values.
 */
 
static struct Definition Qualifier[] =
{
   {"ALT",20},
   {"AMIGA",22},
   {"CAPSLOCK",2},
   {"CONTROL",3},
   {"INTERRUPT",10},
   {"KEYUP",31},
   {"LALT",4},
   {"LAMIGA",6},
   {"LBUTTON",14},
   {"LCOMMAND",6},
   {"LSHIFT",0},
   {"MBUTTON",12},
   {"MULTIBROADCAST",11},
   {"NUMERICPAD",8},
   {"RALT",5},
   {"RAMIGA",7},
   {"RBUTTON",13},
   {"RCOMMAND",7},
   {"RELATIVEMOUSE",15},
   {"REPEAT",9},
   {"RSHIFT",1},
   {"SHIFT",16},
};
#define MAXQUALIFIER    (sizeof(Qualifier)/sizeof(struct Definition))


/*
 *  AsciiToKeyCode[] maps ASCII characters to keyboard scan-codes.  This array 
 *  is in ASCII order.  SHIFT indicates that one of the shift keys must be 
 *  held down together with the correct key in order to produce that ASCII 
 *  character.  Note, however, that upper- and lower-case letters both are 
 *  mapped to un-shifted keys.
 */

static UBYTE AsciiToKeyCode[] =
{
   0x01 | SHIFT,    /* ! */
   0x2A | SHIFT,    /* " */
   0x03 | SHIFT,    /* # */
   0x04 | SHIFT,    /* $ */
   0x05 | SHIFT,    /* % */
   0x07 | SHIFT,    /* & */
   0x2A,            /* ' */
   0x09 | SHIFT,    /* ( */
   0x0A | SHIFT,    /* ) */
   0x08 | SHIFT,    /* * */
   0x0C | SHIFT,    /* + */
   0x38,            /* , */
   0x0B,            /* - */
   0x39,            /* . */
   0x3A,            /* / */
   0x0A,            /* 0 */
   0x01,            /* 1 */
   0x02,            /* 2 */
   0x03,            /* 3 */
   0x04,            /* 4 */
   0x05,            /* 5 */
   0x06,            /* 6 */
   0x07,            /* 7 */
   0x08,            /* 8 */
   0x09,            /* 9 */
   0x29 | SHIFT,    /* : */
   0x29,            /* ; */
   0x38 | SHIFT,    /* < */
   0x0C,            /* = */
   0x39 | SHIFT,    /* > */
   0x3A | SHIFT,    /* ? */
   0x02 | SHIFT,    /* @ */
   0x20,            /* A */
   0x35,            /* B */
   0x33,            /* C */
   0x22,            /* D */
   0x12,            /* E */
   0x23,            /* F */
   0x24,            /* G */
   0x25,            /* H */
   0x17,            /* I */
   0x26,            /* J */
   0x27,            /* K */
   0x28,            /* L */
   0x37,            /* M */
   0x36,            /* N */
   0x18,            /* O */
   0x19,            /* P */
   0x10,            /* Q */
   0x13,            /* R */
   0x21,            /* S */
   0x14,            /* T */
   0x16,            /* U */
   0x34,            /* V */
   0x11,            /* W */
   0x32,            /* X */
   0x15,            /* Y */
   0x31,            /* Z */
   0x1A,            /* [ */
   0x0D | SHIFT,    /* \ */
   0x1B,            /* ] */
   0x06 | SHIFT,    /* ^ */
   0x0B | SHIFT,    /* _ */
   0x00,            /* ` */
   0x20,            /* a */
   0x35,            /* b */
   0x33,            /* c */
   0x22,            /* d */
   0x12,            /* e */
   0x23,            /* f */
   0x24,            /* g */
   0x25,            /* h */
   0x17,            /* i */
   0x26,            /* j */
   0x27,            /* k */
   0x28,            /* l */
   0x37,            /* m */
   0x36,            /* n */
   0x18,            /* o */
   0x19,            /* p */
   0x10,            /* q */
   0x13,            /* r */
   0x21,            /* s */
   0x14,            /* t */
   0x16,            /* u */
   0x34,            /* v */
   0x11,            /* w */
   0x32,            /* x */
   0x15,            /* y */
   0x31,            /* z */
   0x1A | SHIFT,    /* { */
   0x0D | SHIFT,    /* | */
   0x1B | SHIFT,    /* } */
   0x00 | SHIFT,    /* ~ */
};
#define MAXASCII    sizeof(AsciiToKeyCode)


/*
 *  Key[] maps key names to their keyboard scan-codes.  This array is sorted
 *  by name.  SHIFT means that a SHIFT key must be held down in addition to
 *  the indicated key.  Some keys have more than one name (e.g., ESCAPE is
 *  equivalent to ESC).  Note that pressing a qualifier key also can be
 *  detected.
 */

static struct Definition Key[] =
{
   {"BACKSPACE",0x41},
   {"BS", 0x41},
   {"CAPSLOCKKEY",0x62},
   {"COLON",0x29 | SHIFT},
   {"COMMA",0x38},
   {"CONTROLKEY",0x63},
   {"DASH",0x0B},
   {"DEL",0x46},
   {"DELETE",0x46},
   {"DOT",0x3C},
   {"DOWNARROW",0x4D},
   {"ENTER",0x42},
   {"ESC",0x45},
   {"ESCAPE",0x45},
   {"F1",0x50},
   {"F10",0x59},
   {"F2",0x51},
   {"F3",0x52},
   {"F4",0x53},
   {"F5",0x54},
   {"F6",0x55},
   {"F7",0x56},
   {"F8",0x57},
   {"F9",0x58},
   {"HELP",0x5F},
   {"KP0",0x0F},
   {"KP1",0x1D},
   {"KP2",0x1E},
   {"KP3",0x1F},
   {"KP4",0x2D},
   {"KP5",0x2E},
   {"KP6",0x2F},
   {"KP7",0x3D},
   {"KP8",0x3E},
   {"KP9",0x3F},
   {"LALTKEY",0x64},
   {"LAMIGAKEY",0x66},
   {"LCOMMANDKEY",0x66},
   {"LEFTARROW",0x4F},
   {"LSHIFTKEY",0x60},
   {"MINUS",0x4A},
   {"RALTKEY",0x65},
   {"RAMIGAKEY",0x67},
   {"RCOMMANDKEY",0x67},
   {"RETURN",0x44},
   {"RIGHTARROW",0x4E},
   {"RSHIFTKEY",0x61},
   {"SPACE",0x40},
   {"TAB",0x42},
   {"UPARROW",0x4C},
};
#define MAXKEY      (sizeof(Key)/sizeof(struct Definition)) 


/*
 *  Action[] maps key action names to their action numbers (used by the
 *  input handler to perform the action when the key is pressed).  This array
 *  is sorted by name.
 */

static struct Definition Action[] =
{
   {"BACK-WINDOW-TO-FRONT", BACKTOFRONT},
   {"FRONT-WINDOW-TO-BACK", FRONTTOBACK},
   {"NEXT-WINDOW",          ACTIVATENEXT},
   {"PREVIOUS-WINDOW",      ACTIVATEPREVIOUS},
   {"SCREEN-TO-BACK",       SCREENTOBACK},
   {"SCREEN-TO-FRONT",      SCREENTOFRONT},
   {"WINDOW-TO-BACK",       WINDOWTOBACK},
   {"WINDOW-TO-FRONT",      WINDOWTOFRONT},
};
#define MAXACTION   (sizeof(Action)/sizeof(struct Definition))
#endif

/*
 *  Some shorthand macros used to define the default key layout
 */

#define RAMIGA      IEQUALIFIER_RCOMMAND
#define RSHIFT      IEQUALIFIER_RSHIFT

#define UPARROW     0x4C
#define DOWNARROW   0x4D
#define RIGHTARROW  0x4E
#define LEFTARROW   0x4F

#define HOTKEY(q,c,a)  {{c,0,q},{0xFF,a,RAMIGA|RSHIFT}}


/*
 *  DefaultKey[] maps the default key layout.  This array is sorted by
 *  KeyCode value.  Change this array to change the default key bindings.
 */

static struct HotKey DefaultKey[] =
{
   HOTKEY(          RAMIGA, UPARROW,    WINDOWTOFRONT),
   HOTKEY( RSHIFT | RAMIGA, UPARROW,    SCREENTOFRONT),
   HOTKEY(          RAMIGA, DOWNARROW,  WINDOWTOBACK),
   HOTKEY( RSHIFT | RAMIGA, DOWNARROW,  SCREENTOBACK),
   HOTKEY(          RAMIGA, RIGHTARROW, ACTIVATENEXT),
   HOTKEY( RSHIFT | RAMIGA, RIGHTARROW, FRONTTOBACK),
   HOTKEY(          RAMIGA, LEFTARROW,  ACTIVATEPREVIOUS),
   HOTKEY( RSHIFT | RAMIGA, LEFTARROW,  BACKTOFRONT),
};
#define DEFAULTSIZE (sizeof(DefaultKey)/sizeof(struct HotKey))


#ifndef NO_FILE
/*
 *  Error()
 *
 *  Print an error message and the line number where the error occured.
 *  Return the error value.
 */

static int Error(s,x1,x2,x3)
char *s, *x1,*x2,*x3;
{
   printf("Line %2d:  ",LineCount);
   printf(s,x1,x2,x3);
   printf("\n");
   return(BADVALUE);
}


/*
 *  GetNextWord()
 *
 *  Isolate the next word in the line read from the file.
 *  If we are not at the end of the line, then
 *    skip over leading spaces,
 *    set CurWord to point to the beginning of the word,
 *    while we are not at the end of the word,
 *      check if the current character is a word delimiter:
 *      if it is a space or a tab,
 *        skip additional spaces or tabs,
 *        set the terminator character,
 *        and end the word.  
 *        (At this point, CurPos will be pointing to the "real" delimiter,
 *        or to the beginning of the next word, if the delimiter really was
 *        a space).
 *      if it was a NULL, convert it to a new-line.
 *      if it was a dash, comma, colon or new-line,
 *        save the termination character for later,
 *        replace the charactger with a NULL so that CurWord will end at
 *          the end of the word,
 *        and stop looking for more of the word.
 *      otherwise
 *       if we're still looking for the end of the word (i.e., we have not
 *         ended, it by hitting a space),
 *         if its unprintable or the current word is more than one character
 *           long and the current character is not alphanumeric,
 *           then record the bad character and end the word
 *         go on to the next character (i.e., add the current one to the word)
 */
 
static void GetNextWord()
{
   short NotDone = TRUE;
   char c;

   if (TerminationChar != '\n')
   {
      while (*CurPos == ' ' || *CurPos == '\t') CurPos++;
      CurWord = CurPos;
      while (NotDone)
      {
         if (*CurPos == ' ' || *CurPos == '\t')
         {
            *CurPos = '\0';
            while (*(++CurPos) == ' ' || *CurPos == '\t');
            TerminationChar = ' ';
            NotDone = FALSE;
         }
         switch(c = *CurPos)
         {
            case '\0':
               c = *CurPos = '\n';
            case '-':
            case ',':
            case ':':
            case '\n':
               TerminationChar = c;
               *CurPos++ = '\0';
               NotDone = FALSE;
               break;

            default:
               if (NotDone)
               {
                  if (NOTPRINTABLE(c) || (CurPos != CurWord && NOTALPHANUM(c)))
                  {
                     TerminationChar = c;
                     NotDone = FALSE;
                  }
                  CurPos++;
               }
               break;
         }
      }
   }
}


/*
 *  FindWord()
 *
 *  Search the KeyWord array (containing Count entries) for an entry with
 *  Name equal to theWord.  Return the corresponding Code value, or BADVALUE
 *  if theWord does not appear in the KeyWord array.
 *
 *  KeyWord[] must be sorted by name, since we use a binary search to find
 *  theWord witin it.
 */

static int FindWord(theWord,KeyWord,Count)
char *theWord;
struct Definition KeyWord[];
int Count;
{
   int value = BADVALUE;
   register short Min,Max, Num;
   register int comp;
   
   Max = Count; Min = -1;
   while ((Num = (Min + Max) >> 1) != Min && value == BADVALUE)
   {
      comp = stricmp(theWord,KeyWord[Num].Name);
      if (comp < 0) Max = Num; else if (comp > 0) Min = Num;
         else value = KeyWord[Num].Code;
   }
   return(value);
}


/*
 *  FindQualifier()
 *
 *  Find a qualifier name in the Qualifier[] array.
 */
#define FindQualifier(w)    FindWord(w,Qualifier,MAXQUALIFIER)


/*
 *  FindKeyCode()
 *
 *  Find the scan-code for the given key name.  If the key name is a single
 *  ASCII character, use the AsciiToKeyCode array to look up the scan-code
 *  directly, otherwise use FindWord() to search the Key[] array.
 */

static int FindKeyCode(theWord)
char *theWord;
{
   int value = BADVALUE;
   
   if (strlen(theWord) == 1)
   {
      if (PRINTABLE(*theWord)) value = AsciiToKeyCode[*theWord-'!'];
   } else {
      value = FindWord(theWord,Key,MAXKEY);
   }
   return(value);
}


/*
 *  GetKeyCode()
 *
 *  Parse a set of qualifiers and a key name and return the KeyCode longword
 *  that describes the designated key.  SHIFT, AMIGA, and ALT flags are 
 *  used to indicate when more than one key is designated.  Return BADVALUE
 *  if there is an error parsing the line.
 *
 *  Get the next word on the line, and check the termination character.  
 *  Qualifiers end with dashes, commas, or spaces; key-names end with a colon.
 *  Try to find the qualifier or key-name in the proper list, and give an error
 *  if it can not be found, or if the name is null.  For a qualifier, set its
 *  flag bit in the KeyCode longword.  For key-names, set the scan-code and
 *  the set the SHIFT bit in the qualifier flag bits if necessary.
 *  If the end of the line is reached, display an error.  If some other
 *  delimiter was found, then display an error (making unprintable characters
 *  printable).
 *
 *  Once a key-name is found, stop looking for more words.
 */

static void GetKeyCode(theKey)
long *theKey;
{
   short NotDone = TRUE;
   int value;

   *theKey = 0;
   while (NotDone)
   {
      GetNextWord();
      switch(TerminationChar)
      {
         case '-':
         case ',':
         case ' ':
            if (strlen(CurWord))
            {
               value = FindQualifier(CurWord);
               if (value > BADVALUE)
                  *theKey |= (1 << value);
                 else
                  *theKey = Error("Unrecognized key qualifier '%s'",CurWord);
            } else {
               *theKey = Error("Missing qualifier keyword");
            }
            break;

         case ':':
            if (strlen(CurWord))
            {
               value = FindKeyCode(CurWord);
               if (value > BADVALUE)
               {
                  *theKey |= ((value & (~SHIFT)) << 24) |
                             ((value & SHIFT) << 9);
               } else {
                  *theKey = Error("Unrecognized key name '%s'",CurWord);
               }
            } else {
               *theKey = Error("Key name not specified");
            }
            NotDone = FALSE;
            break;

         case '\n':
            *theKey = Error("Key name ends prematurely");
            NotDone = FALSE;
            break;

         default:
            if ((TerminationChar & 0x7F) >= ' ')
               *theKey = Error("Illegal delimiter character '%c'",
                  (char)TerminationChar);
              else
               *theKey = Error("Illegal delimiter character '^%c'",
                  (char)((TerminationChar & 0x7F) + '@'));
            break;
      }
   }
}


/*
 *  GetKeyAction()
 *
 *  Parse the rest of the input line for a key-action keyword.  First, remove
 *  leading and training blanks, and replace the final new-line with a NULL.
 *  Look up the remainder of the line (if any) in the Action[] array, and
 *  if it is not found, report the error.
 */

static void GetKeyAction(theAction)
short *theAction;
{
   *theAction = BADVALUE;

   while (*CurPos == ' ' || *CurPos == '\t') CurPos++;
   CurWord = CurPos;
   CurPos = CurPos + strlen(CurWord) - 1;
   if (*CurPos == '\n') *CurPos-- = '\0';
   while (*CurPos == ' ' || *CurPos == '\t') *CurPos-- = '\0';

   if (*CurWord)
   {
      *theAction = FindWord(CurWord,Action,MAXACTION);
      if (*theAction == BADVALUE) Error("Unrecognized action '%s'",CurWord);
   } else {
      Error("No action specified");
   }
}


/*
 *  KeyCompare()
 *
 *  Return a positive number if the first key is bigger than the second,
 *  zero if they are equal, and a negative number if the second key is 
 *  bigger.  This routine is used by mSort() to sort the keys.
 */

static int KeyCompare(key1,key2)
struct HotKeyItem *key1, *key2;
{
   return(key1->hki_KeyCode - key2->hki_KeyCode);
}


/*
 *
 *  KeyDispose()
 *
 *  Remove a duplicate key definition and report the fact that a key is
 *  multiply defined (it takes a little work to get the qualifier and key
 *  names back out of the arrays).  This routine is called by mSort() when
 *  it finds duplicate keys.
 */

static void KeyDispose(key)
struct HotKeyItem *key;
{
   short i;
   UBYTE code = key->hki_Code & 0x7F;
   int KeyNotFound = TRUE;
   long mask = 0xFFFFFFFF;

   printf("Key ");
   for (i=0; i<MAXQUALIFIER; i++)
      if (key->hki_KeyCode & (1 << Qualifier[i].Code) & mask)
      {
         printf("%s-",Qualifier[i].Name);
         mask &= ~(1 << Qualifier[i].Code);
      }
   for (i=0; i<MAXKEY && KeyNotFound; i++)
      if (code == Key[i].Code)
      {
         printf("%s",Key[i].Name);
         KeyNotFound = FALSE;
      }
   for (i=0; i<MAXASCII && KeyNotFound; i++)
   {
      if (code == AsciiToKeyCode[i])
      {
         printf("%c",i+'!');
         KeyNotFound = FALSE;
      }
   }
   printf(" multiply defined\n");
   KeyCount--;
   key->Next = key->Prev = NULL;
   FreeMem(key,sizeof(*key));
}


/*
 *  GetKeyList()
 *
 *  Read each line from the file (incrementing the line count as we go), 
 *  and skip leading spaces and blank lines.  Get the key code and key action
 *  specified on the line.  If no error was found, add the key definition 
 *  to the linked list of keys defined.  If SHIFT, AMIGA, or ALT were specified,
 *  then add one key each for the left and right version of that qualifier.
 */

static void GetKeyList()
{
   long theKey;
   short theAction;
   struct HotKeyItem *TempKey;
   UWORD mask,multikeys;
   extern char *fgets();

   while (feof(InFile) == FALSE)
   {
      CurPos = fgets(InputLine,LINESIZE,InFile);
      TerminationChar = '\0';
      LineCount++;
      while (*CurPos == ' ' || *CurPos == '\t') CurPos++;

      if (CurPos != NULL && *CurPos != '\0' && *CurPos != '\n')
      {
         GetKeyCode(&theKey);
         GetKeyAction(&theAction);
         if (theKey != BADVALUE && theAction != BADVALUE)
         {
            multikeys = (theKey >> 15) & 0xFE;
            multikeys |= multikeys << 1;
            if (multikeys == 0) multikeys = 1;
            for (mask=1; multikeys; mask<<=1,multikeys>>=1)
            {
               if (multikeys & 1)
               {
                  NEW(HotKeyItem,TempKey);
                  TempKey->hki_KeyCode = theKey;
                  TempKey->hki_Flags   = 0;
                  TempKey->hki_Qual   |= mask >> 1;
                  TempKey->hki_KeyMask = 0xFFFFFFFF;
                  TempKey->hki_Action  = theAction;
                  TempKey->Next = KeyList;
                  KeyList = TempKey;
                  KeyCount++;
               }
            }
         }
      }
   }
}


/*
 *  SetKeyMasks()
 *
 *  For each key scan-code, we OR together the qualifier masks for all the
 *  definitions for that scan-code and set the key Mask value for each
 *  key with that scan-code to the final ORed mask.  That is, the Mask value
 *  indicates what qualifiers are important for determining when a key has
 *  been pressed (and distinguishing it from other definitions using the
 *  same scan-code but different qualifiers).
 */

static void SetKeyMasks()
{
   struct HotKeyItem *CurKey = KeyList;
   struct HotKeyItem *LastKey = CurKey;
   UWORD Mask;

   while (LastKey)
   {
      Mask = 0;
      while (CurKey && CurKey->hki_Code == LastKey->hki_Code)
      {
         Mask |= CurKey->hki_Qual;
         CurKey = CurKey->Next;
      }
      if (Mask == 0) Mask = KEYMASK;
      do
      {
         LastKey->hki_Mask = Mask;
         LastKey = LastKey->Next;
      } while (LastKey != CurKey);
   }
}


/*
 *  MakeKeyArray()
 *
 *  If there are any keys defined, allocate enough space for the KeyArray
 *  that will contain the key definitions, then go through the list and
 *  copy the definitions into the array.  Free each item from the list once it
 *  is copied.  The array saves space (it does not need to contain pointers 
 *  to next and previous items), and allows for easy implementation of a 
 *  binary search on the array.
 */

static void MakeKeyArray()
{
   struct HotKeyItem *TempKey;
   short i;

   if (KeyCount)
   {
      KeyArray = (struct HotKey *)New("KeyArray",KEYARRAYSIZE);
      for (i=0; i<KeyCount; i++)
      {
         KeyArray[i].hk_KeyCode = KeyList->hki_KeyCode;
         KeyArray[i].hk_KeyMask = KeyList->hki_KeyMask;
         TempKey = KeyList; KeyList = KeyList->Next;
         FreeMem(TempKey,sizeof(*TempKey));
      }
   }
}
#endif


/*
 *  MakeDefaultArray()
 *
 *  Copy the DefaultKey[] array into a dynamically allocated array that can
 *  be passed to the input handler and still remain in memory even when the
 *  original process is unloaded.
 */
 
static void MakeDefaultArray()
{
   short i;
   
   KeyCount = DEFAULTSIZE;
   KeyArray = (struct HotKey *)New("KeyArray",KEYARRAYSIZE);
   for (i=0; i<KeyCount; i++)
   {
      KeyArray[i].hk_KeyCode = DefaultKey[i].hk_KeyCode;
      KeyArray[i].hk_KeyMask = DefaultKey[i].hk_KeyMask;
   }
}


/*
 *  GetKeyArray()
 *
 *  If there is a command-line argument, then 
 *    it must be a file name, so try to open it (error if there is a problem).
 *    Create the key definition list from the lines in the file.
 *    If there were no valid key definitions, 
 *      say so,
 *     otherwise, 
 *      sort the key list,
 *      set the key masks for each scan-code,
 *      make the key array from the sorted list.
 *   otherwise (there was no file name given, so)
 *    make the key array from the default key list.
 */

void GetKeyArray(argc,argv)
int argc;
char *argv[];
{
#ifndef NO_FILE
   extern struct HotKeyItem *mSort(); 

   if (argc > 1)
   {
      InFile = fopen(argv[1],"r");
      if (InFile == NULL)
         DoExit("Can't Open File '%s':  Error %d",argv[1],ERROR);
      GetKeyList();
      if (KeyCount == 0)
      {
         DoExit("No valid key definitions found in file '%s'",argv[1]);
      } else {
         KeyList = mSort(KeyList,KeyCompare,KeyDispose);
         SetKeyMasks();
         MakeKeyArray();
      }
   } else
#endif
   {
      MakeDefaultArray();
   }
}
