/******************************************************************************

    MODUL
	keytables.c

    DESCRIPTION
	Management for Key-Hashing tables

    NOTES
	original from M.Dillon

	for the first(!) call of any function of that module,
	the corresponding HASH-table must be clean (setmem(...,0))

    BUGS
	thousands - but where
	there may be some inconsistencies

    TODO
	new method for management of hashes
	resolve the references to av[]

    EXAMPLES

    SEE ALSO
	keycodes.c keycom.c keycontrol.c

    INDEX

    HISTORY
	14-Dec-92  b_null created
	15-Dez-92  b_null modified
	15-Dez-92  b_null docs updated, some checks added
	16-Dez-92  b_null documented

******************************************************************************/

/**************************************
		Includes
**************************************/
#include "defs.h"
#include <devices/keymap.h>
#include <devices/console.h>
#include "keyhashes.h"


/**************************************
	    Globale Variable
**************************************/

Prototype void	 keyload	(KEYTABLE * kt, char * name);
Prototype void	 keysave	(KEYTABLE * kt, char * name);
Prototype int	 loadkeys	(KEYTABLE * kt, FILE * fi, int * lineno);
Prototype int	 savekeys	(KEYTABLE * kt, FILE * fo);
Prototype HASH * findhash	(KEYTABLE * kt, UBYTE code, UWORD qual);
Prototype APTR	 keyspec2macro	(KEYTABLE * kt, char * str);
Prototype int	 resethash	(KEYTABLE * kt);
Prototype int	 unmapkey	(KEYTABLE * kt, char * key);
Prototype int	 mapkey 	(KEYTABLE * kt, char * key, char * value);
Prototype int	 remhash	(KEYTABLE * kt, UBYTE code, UWORD mask, UWORD qual);
Prototype int	 addhash	(KEYTABLE * kt, UBYTE code, UWORD mask, UWORD qual, const UBYTE * comm, const UBYTE * help);
Prototype void	 dealloc_hash	(KEYTABLE * kt);

Prototype KEYTABLE * get_keytable    (char * name);
Prototype KEYTABLE * new_keytable    (char * name, int defaults);
Prototype void	     delete_keytable (KEYTABLE * kt, int force);

Prototype void	     exit_keytables (void);

/**************************************
      Interne Defines & Strukturen
**************************************/

#define HASHMASK  (HASHSIZE-1)
#define QUALMASK  0x3ff


/**************************************
	    Interne Variable
**************************************/
/* extern TWOSTRINGS defmap[]; */
#include "defmap.h"


/* this is the list of all keytables */
static struct MinList KeyTables = { (struct MinNode *)&KeyTables.mlh_Tail,
	NULL, (struct MinNode *)&KeyTables.mlh_Head };

/**************************************
	   Interne Prototypes
**************************************/



/*****************************************************************************

    NAME
	freehash

    PARAMETER
	HASH * hash

    RESULT
	the hash->next entry if existing
	NULL if hash == NULL

    RETURN
	HASH *

    DESCRIPTION
	free a hash-entry and all associated data

    NOTES
	the free'd hash must not be entry in a hashtable,
	as we do not take care of any refernces, but only
	of hash, comm and help

    BUGS

    EXAMPLES

    SEE ALSO

    INTERNALS

    HISTORY
	23 Jan 1993 b_null created

******************************************************************************/

HASH * freehash (HASH * hash)
{
    HASH * next = NULL;

    if (hash) {
	next = (HASH *)hash->next;
	if (hash->comm) {
	    DeallocFunc (hash->comm);
	} /* if */
	if (hash->help) {
	    DeallocFunc (hash->help);
	} /* if */
	FreeFunc(hash, sizeof(HASH));
    } /* if */

    return (next);
} /* freehash */



/*****************************************************************************

    NAME
	addhash

    PARAMETER
	KEYTABLE * kt
	UBYTE	code
	UWORD  mask
	UWORD  qual
	char  * comm
	char  * help

    RESULT
	success: RET_SUCC == ok; RET_FAIL == failure

    RETURN
	int

    DESCRIPTION
	create a new hash-entry for a known hash
	if there is already an existing one with the same code/qual
	simply change its command

    NOTES
	that function is called by mapkey

    BUGS
	WARNING : WE DO NOT LOOK FOR A SETTING OF help YET !!!

    EXAMPLES

    SEE ALSO

    INTERNALS
	finde den hash-slot
	falls der eintrag schon existiert, ersetze den comm
	sonst erstelle einen neuen eintrag
	falls nicht alles funktionierte, stelle den alten zustand wieder her

    HISTORY
	14-Dec-92  b_null included & modified
	16-Dec-92  b_null added handling of "nolink"

******************************************************************************/

int addhash (KEYTABLE * kt, UBYTE code, UWORD mask, UWORD qual, const UBYTE * comm, const UBYTE * help)
{
    HASH ** p,
	  * hash;
//    APTR    oldcomm = NULL;

    if (kt == NULL) {
	return (RET_FAIL);
    } /* if empty or missing table */

    hash = *(p = &kt->hash[code&HASHMASK]);

    while (hash) {
	if (hash->code == code && hash->qual == qual && hash->mask == mask) {
	    goto newstr;
	} /* if found old */
	hash = *(p = (HASH **)&hash->next);
    } /* while entries left */

    hash  = (HASH *)AllocFunc (sizeof (HASH), MEMF_ANY);
    memset (hash, 0, sizeof (HASH));

    if (hash == NULL) {
	globalflags.Abortcommand = 1;
	return (RET_FAIL);
    } /* if nomemory */

    hash->next = NULL;
    hash->code = code;
    hash->mask = mask;
    hash->qual = qual;

newstr:
    /* oldcomm = hash->comm; */
    DeallocFunc (hash->comm);
    DeallocFunc (hash->help);

    hash->comm = DupFunc (comm, 0);
    hash->help = DupFunc (help, 0);

    if ((!hash->comm) /* || (!hash->help) */ /* || (oldcomm == hash->comm) */) {
	*p = freehash (hash);
	return (RET_FAIL);
    } /* if something went wrong */

    /* everything worked fine - insert it into the table */
    *p = hash;
    return (RET_SUCC);
} /* addhash */



/*****************************************************************************

    NAME
	remhash

    PARAMETER
	KEYTABLE * kt
	UBYTE	code
	UWORD  mask
	UWORD  qual

    RESULT
	success:
	    RET_SUCC == ok;
	    RET_FAIL == failure or not found

    RETURN
	int

    DESCRIPTION
	delete an existing hashentry

    NOTES
	that function is called by unmapkey

    BUGS

    EXAMPLES

    SEE ALSO

    INTERNALS
	finde den hash-slot
	falls der eintrag existiert, loesche ihn und seinen link
	sonst mache gar nichts

    HISTORY
	14-Dec-92  b_null included & modified
	23-Jan-93  b_null used freehash

******************************************************************************/

int remhash (KEYTABLE * kt, UBYTE code, UWORD mask, UWORD qual)
{
    HASH *  hash, /* current */
	 ** p;	  /* previous */

    if (kt == NULL) {
	return (RET_FAIL);
    } /* if empty or missing table */

    hash = *(p = &kt->hash[code&HASHMASK]);
    while (hash) {
	if (hash->code == code && hash->qual == qual && hash->mask == mask) {
	    *p = freehash (hash);
	    return   (RET_SUCC);
	} /* if found */
	hash = *(p = (HASH **)&hash->next);
    } /* while searching */

    /* not found: */
    return (RET_FAIL);
} /* remhash */



/*****************************************************************************

    NAME
	mapkey

    PARAMETER
	KEYTABLE * kt
	char *	key
	char *	value

    RESULT
	success:
	    RET_FAIL == error
	    RET_OK   == everything ok

    RETURN
	int

    DESCRIPTION
	first abstraction for addhash
	which uses an ascii-name

    NOTES
	this function might call error()

    BUGS

    EXAMPLES

    SEE ALSO
	addhash, unmapkey

    INTERNALS
	falls es sich um einen gueltigen map handelt
	    belege ihn (neu)
	sonst gebe eine fehlermeldung aus

    HISTORY
	14-Dec-92  b_null included & modified
	23-Jan-93  b_null void -> int

******************************************************************************/

int mapkey (KEYTABLE * kt, char * key, char * value)
/* char * help */
{
    UBYTE  code;
    UWORD qual;

    if (get_codequal (key, &code, &qual)) {
	return (addhash (kt, code, QUALMASK, qual, value, NULL)); /* help)); */
    } else {
	error ("%s:\nUnknown Key '%s'", CommandName(), key);
    }
    return (RET_FAIL);
} /* mapkey */



/*****************************************************************************

    NAME
	unmapkey

    PARAMETER
	KEYTABLE * kt
	char *	key

    RESULT
	success:
	    RET_FAIL == error
	    RET_OK   == everything ok

    RETURN
	int

    DESCRIPTION
	first abstraction for remhash
	which uses an ascii-name

    NOTES
	this function might call error()

    BUGS

    EXAMPLES

    SEE ALSO
	remhash, mapkey

    INTERNALS
	falls es sich um einen gueltigen map handelt
	    loesche seine belegung
	sonst gebe eine fehlermeldung aus

    HISTORY
	14-Dec-92  b_null included & modified
	23-Jan-93  b_null void -> int

******************************************************************************/

int unmapkey (KEYTABLE * kt, char * key)
{
    UBYTE  code;
    UWORD qual;

    if (get_codequal(key, &code, &qual)) {
	return (remhash (kt, code, QUALMASK, qual));
    } else {
	error ("%s:\nUnknown Key '%s'", CommandName(), key);
    }
    return (RET_FAIL);
} /* unmapkey */



/*****************************************************************************

    NAME
	dealloc_hash

    PARAMETER
	KEYTABLE * kt

    RESULT
	-/-

    RETURN
	void

    DESCRIPTION
	frees all Hash-entries in a key-table

    NOTES

    BUGS

    EXAMPLES

    SEE ALSO

    INTERNALS
	ueber allen hash-slots
	    loesche jeden eintrag des hash-slots

    HISTORY
	14-Dec-92  b_null included & modified

******************************************************************************/

void dealloc_hash ( KEYTABLE * kt )
{
    HASH * hash,
	 * hnext = NULL;  /* save ptr to the next entry when freeing the current */
    WORD   i;

    if (kt == NULL) {
	return;
    } /* if empty or missing table */

    for (i = 0; i < HASHSIZE; i++) {
	for (hash = kt->hash[i]; hash; hash = hnext) {
	    hnext = freehash (hash);
	} /* for */
	kt->hash[i] = NULL;
    } /* for */
} /* dealloc_hash */



/*****************************************************************************

    NAME
	resethash

    PARAMETER
	KEYTABLE * kt

    RESULT
	success:
	    RET_FAIL == error (might have worked partially)
	    RET_OK   == everything ok

    RETURN
	int

    DESCRIPTION
	dealloc all entries of a hashtable and then
	set its contents to default-values

    NOTES
	the defaults are stored in an array
	called defmap

    BUGS

    EXAMPLES

    SEE ALSO
	mapkey, dealloc_hash

    INTERNALS
	loesche die tabelle
	solange noch ein gueltiger eintrag in defmap ist (und alles gutging)
	    erzeuge eine map fuer ihn

    HISTORY
	14-Dec-92  b_null included
	16-Dec-92  b_null documented

******************************************************************************/

int resethash (KEYTABLE * kt)
{
    WORD i;

    if (kt == NULL) {
	return (RET_FAIL);
    } /* if empty or missing table */

    dealloc_hash (kt);

    for (i = 0; defmap[i].from; ++i) {
	if (!mapkey (kt, defmap[i].from, defmap[i].to)) { /* ,defmap[i].help)) */
	    return (RET_FAIL);
	} /* if error */
    } /* for defmap-entries */

    return (RET_SUCC);
} /* resethash */



/*****************************************************************************

    NAME
	findhash

    PARAMETER
	KEYTABLE * kt
	UBYTE	code
	UWORD  qual

    RESULT
	the hash-entry associated with code and qual

    RETURN
	HASH *

    DESCRIPTION
	simple searching

    NOTES
	this routine is part of keyspec2macro

    BUGS

    EXAMPLES

    SEE ALSO
	keyspec2macro

    INTERNALS
	finde den richtigen slot
	solange es noch eintraege in dem slot gibe, und der richtige nicht
	    gefunden ist,
	    gehe zum naechsten eintrag
	wenn du was gefunden hast, gib es aus, sonst NULL

    HISTORY
	16-Dec-92  b_null created

******************************************************************************/

HASH * findhash (KEYTABLE * kt, UBYTE code, UWORD qual)
{
    HASH * hash;

    if (kt == NULL) {
	return (NULL);
    } /* if empty or missing table */

    for (hash = kt->hash[code&HASHMASK]; hash; hash = (HASH *)hash->next) {
	if (hash->code == code) {
	    if (hash->qual == (qual & hash->mask)) {
		return (hash);
	    }
	}
    }
    return (NULL);
} /* findhash */



/*****************************************************************************

    NAME
	keyspectohash

    PARAMETER
	KEYTABLE * kt
	char *	str

    RESULT
	the Hashentry associated with
	the name str

    RETURN
	HASH *

    DESCRIPTION
	transform str into a Hash-value and search for its entry

    NOTES
	this routine uses findhash

    BUGS

    EXAMPLES

    SEE ALSO
	findhash

    INTERNALS
	falls es sich um einen gueltigen schluessel handelt,
	    finde eine passenden eintrag und
		gib ihn aus
	sonst gib NULL aus

    HISTORY
	23-Jan-93  b_null created

******************************************************************************/

HASH * keyspectohash (KEYTABLE * kt, char * str)
{
    UBYTE  code;
    UWORD qual;

    if (get_codequal(str, &code, &qual)) {
	return (findhash (kt, code, qual));
    } /* if is key */
    return (NULL);
} /* keyspectohash */



/*****************************************************************************

    NAME
	keyspec2macro

    PARAMETER
	KEYTABLE * kt
	char *	str

    RESULT
	the macro-field of the Hashentry associated with
	the name str

    RETURN
	APTR

    DESCRIPTION
	transform str into a Hash-value and search for its entry
	then return its command-entry

    NOTES
	this routine is splitted into
	two minor ones by putting the searching
	into another routine called findhash&keyspectohash

    BUGS

    EXAMPLES

    SEE ALSO
	findhash

    INTERNALS
	finde den passendedn eintrag
	    und gib dessen comm aus

    HISTORY
	14-Dec-92  b_null included
	16-Dec-92  b_null splitted & documented

******************************************************************************/

APTR keyspec2macro (KEYTABLE * kt, char * str)
{
    HASH *hash;

    hash = keyspectohash (kt, str);
    if (hash) {
	return (hash->comm);
    } /* if */
    return (NULL);
} /* keyspec2macro */


/*****************************************************************************

    NAME
	savekeys

    PARAMETER
	KEYTABLE * kt
	FILE *	fi

    RESULT
	success:
	    RET_FAIL == error
	    RET_OK   == everything ok

    RETURN
	int

    DESCRIPTION
	write a special keymap file
	or the keymap section of a file
	such a section can be read with loadkeys

    NOTES
	currently the keymap-section is in ascii
	format - this may be changed to IFF
	this function is separated from keysave for use in packages

    BUGS

    EXAMPLES

    SEE ALSO
	loadkeys

    INTERNALS
	schreibe einen header
	gehe alle slots entlang und
	    schreibe alle eintraege der slots
	schreibe einen footer

    HISTORY
	14-Dec-92  b_null included
	16-Dec-92  b_null documented

******************************************************************************/

int savekeys (KEYTABLE * kt, FILE * fo)
{
    HASH * hash;
    int    i;

    if (kt == NULL) {
	return (RET_FAIL);
    } /* if empty or missing table */

    fprintf(fo, "KEYLIST START\n");
    for (i = 0; i < HASHSIZE; i++) {
	for (hash = kt->hash[i]; hash; hash = (HASH *)hash->next) {
	    fprintf (fo, "\tKEY   %s\n", cqtoa (hash->code, hash->qual));
/* --- THAT BLOCK MUST BE CHANGED IF MACROS ARE MODIFIED */
	 /* fprintf (fo, "\t HELP %s\n", hash->help); */
	    fprintf (fo, "\t COM  %s\n", hash->comm);
/* --- THAT BLOCK MUST BE CHANGED IF MACROS ARE MODIFIED */
	} /* for keys */
    } /* for hashes */
    fprintf(fo, "KEYLIST END\n");
    return (RET_SUCC);
} /* savekeys */



/*****************************************************************************

    NAME
	loadkeys

    PARAMETER
	KEYTABLE * kt
	FILE *	fi
	int  *	lineno

    RESULT
	success:
	    RET_FAIL == error
	    RET_OK   == everything ok

    RETURN
	int

    DESCRIPTION
	read a special Keymap-File
	or the Keymap-section of a file
	which was created with savekeys

    NOTES
	this function might call error()
	this function is separated from keyload for use in packages
	can not support "help" yet

    BUGS

    EXAMPLES

    SEE ALSO
	savekeys

    INTERNALS
	lese den header
	solange alles gutging und der footer nicht erreicht wurde
	    lese nacheinander schluessel
	    und macro
	    falls eine Zeile nicht in diese Sequenz passt ABBRUCH

    das muss geaendert werden:
	lese den header
	solange alles gutging und der footer nicht erreicht wurde
	lese  schluessel und
	danach macro (und evtl help)
	    da die reihenfolge von macro und help variieren kann,
	       darf der naechste addhash erst durchgefuehrt werden beim naechsten KEY oder bei KEYLIST END

    HISTORY
	14-Dec-92  b_null included
	16-Dec-92  b_null documented

******************************************************************************/

#define next_nonblank(buf) {while (*buf && *buf<33) buf++; }

int loadkeys (KEYTABLE * kt, FILE * fi, int * lineno)
{
    char * buf;
    char   nkey   [128];
    char   help   [128];
    char   body   [LINE_LENGTH];

    if (kt == NULL) {
	return (RET_FAIL);
    } /* if empty or missing table */

    buf = getnextcomline (fi, lineno);

    if (!buf) {
	globalflags.Abortcommand = 1;
	return (RET_FAIL);
    } /* if */
    if (strncmp(buf, "KEYLIST START", 13) != 0) {
	error  ("No Keystart header");
	return (RET_FAIL);
    } /* if */

    nkey  [0] = 0;
    help  [0] = 0;
    body  [0] = 0;

    while (globalflags.Abortcommand == 0) {
	buf = getnextcomline(fi, lineno);
/* printf("read %s\n", buf); */
	if (!buf) {
	    globalflags.Abortcommand = 1;
	    return (RET_FAIL);
	} /* if */

	if (strncmp(buf, "KEYLIST END", 11) == 0) {
	    return (RET_SUCC);
	} else if (strncmp(buf, "KEY ", 4) == 0) {
	    buf += 3;
	    next_nonblank(buf);
	    if (nkey[0] != 0) {
		error  ("<%s:\nDeclared Key w/out Body '%s'", CommandName(), buf);
		return (RET_FAIL);
	    } /* if */
	    strncpy(nkey, buf, 128);
#ifdef NOT_DEF
	} else if (strncmp(buf, "HELP", 4) == 0) {
	    buf += 4;
	    next_nonblank(buf);
	    if (nkey[0] == 0) {
		error  ("<%s:\nDeclared Help w/out a Key", CommandName(), buf);
		return (RET_FAIL);
	    } /* if */
	    strncpy(help, buf, 128);
#endif
	} else if (strncmp (buf, "COM", 3) == 0) {
	    buf += 3;
	    next_nonblank(buf);
	    if (nkey[0] == 0) {
		error  ("<%s:\nDeclared Body w/out a Key", CommandName());
		return (RET_FAIL);
	    } /* if */
	    strncpy (body, buf, LINE_LENGTH);
	    mapkey  (kt, nkey, body); /* ,help */
	    help[0] = 0;
	    nkey[0] = 0;
	} else {
	    error  ("%s:\nunknown identifier '%s'", CommandName(), buf);
	    return (RET_FAIL);
	} /* if types */
    } /* while not ready */
    return (RET_FAIL);
} /* loadkeys */


/*****************************************************************************

    NAME
	keysave

    PARAMETER
	KEYTABLE * kt
	char *	name

    RESULT
	-/-

    RETURN
	void

    DESCRIPTION
	first abstraction of savekeys to single files:
	handling of open&close

    NOTES

    BUGS

    EXAMPLES

    SEE ALSO
	savekeys

    INTERNALS
	oeffne ein file
	falls das ging,
	    lese die enthaltenen mappings
	    und schliesse es
	sonst gib einen Fehler aus

    HISTORY
	14-Dec-92  b_null included
	16-Dec-92  b_null renamed & documented

******************************************************************************/

void keysave (KEYTABLE * kt, char * name)
{
    FILE * fo = NULL;

    if (fo = fopen (name, "w")) {
	savekeys (kt, fo);
	fclose (fo);
    } else {
	error ("%s:\nCan't open file %s for output", CommandName(), name);
    } /* if */
} /* keysave */


/*****************************************************************************

    NAME
	keyload

    PARAMETER
	KEYTABLE * kt
	char *	name

    RESULT
	-/-

    RETURN
	void

    DESCRIPTION
	first abstraction of loadkeys to single files:
	handling of open & close

    NOTES

    BUGS

    EXAMPLES

    SEE ALSO
	loadkeys

    INTERNALS
	oeffne ein file
	falls das ging,
	    schreibe die aktuellen mappings hinein
	    und schliesse es
	sonst gib einen Fehler aus

    HISTORY
	14-Dec-92  b_null included
	16-Dec-92  b_null renamed & documented

******************************************************************************/

void keyload (KEYTABLE * kt, char * name)
{
    FILE * fi	  = NULL;
    int    lineno = 0;

    if (fi = fopen (name, "r")) {
	dealloc_hash (kt);
	loadkeys (kt, fi, &lineno);
	fclose (fi);
    } else {
	error ("%s:\nCan't open file %s for input", CommandName(), name);
    } /* if */
} /* keyload */





/*****************************************************************************

    BASIC FUNCTIONS ON KEYTABLES :

	get, new, delete

    all keytables created with new_keytable are deleted at program termination

*****************************************************************************/

/*****************************************************************************

    NAME
	get_keytable

    PARAMETER
	char * name

    RESULT
	the keytable with the name name, if it is already existing
	else NULL

    RETURN
	KEYTABLE *

    DESCRIPTION
	search function on the hidden list of keytables

    NOTES
	useful only, if we are using multiple keytables

    BUGS

    EXAMPLES

    SEE ALSO

    INTERNALS

    HISTORY
	27 Jan 1993 b_null created

******************************************************************************/

KEYTABLE * get_keytable (char * name)
{
    KEYTABLE * kt;

    if (name == NULL) {
	return (GetHead (&KeyTables));
    } /* if wanted default */

    for (kt = GetHead(&KeyTables); kt; kt = GetSucc(kt)) {
	if (strcmp (kt->node.ln_Name, name) == 0) {
	    return (kt);
	} /* if */
    } /* for */
    return (NULL);
} /* get_keytable */



/*****************************************************************************

    NAME
	delete_keytable

    PARAMETER
	KEYTABLE * kt
	int	   force

    RESULT
	-/-

    RETURN
	void

    DESCRIPTION
	delete a full keytable
	that function does NOT dlete the last keytable,
	unless force is set to 1

    NOTES
	useful only, if we are using multiple keytables

	that function does NOT take care of any references to a keytable
	You must do that job on a higher abstraction-level
	    (e.g. in keycom.c - for all ED's if )

    BUGS

    EXAMPLES

    SEE ALSO

    INTERNALS
	ausklinken aus der keytable-liste
	loeschen der hash-entries
	loeschen des namens
	loeschen des bodies

    HISTORY
	27 Jan 1993 b_null created

******************************************************************************/

void delete_keytable (KEYTABLE * kt, int force)
{
    if (kt) {
	Remove	    (&kt->node);
	if ((!force) && (!GetHead (&KeyTables))) {
	    AddHead ((struct List *)&KeyTables, &kt->node);
	    return;
	} /* if */

	dealloc_hash (kt);
	DeallocFunc  (kt->node.ln_Name);
	FreeFunc     (kt, sizeof(KEYTABLE));
    } /* if */
} /* delete_keytable */



/*****************************************************************************

    NAME
	new_keytable

    PARAMETER
	char * name
	int    defaults

    RESULT
	either a new keytable, if there is not already one with that name
	or the first keytable, that matches that name
	if we have to create a new one, we can set defaults to 1 to
	fill in the default-keytable

    RETURN
	KEYTABLE *

    DESCRIPTION

    NOTES
	useful only, if we are using multiple keytables

	if we can fill in only parts of the deafult-information,
	we do NOT fail

    BUGS

    EXAMPLES

    SEE ALSO

    INTERNALS

    HISTORY
	27 Jan 1993 b_null created

******************************************************************************/

KEYTABLE * new_keytable (char * name, int defaults)
{
    KEYTABLE * kt;

    if (kt = get_keytable(name)) {
	return (kt);
    } /* if */

    kt = AllocFunc (sizeof(KEYTABLE), MEMF_ANY);
    if (kt == NULL) {
	return (NULL);
    } /* if */

    setmem (kt, sizeof(KEYTABLE), 0);
    kt->node.ln_Name = DupFunc (name, MEMF_ANY);
    if (kt->node.ln_Name == NULL) {
	FreeFunc (kt, sizeof (KEYTABLE));
	return (NULL);
    } /* if */

    if (defaults) {
	resethash (kt);
    } /* if */

    AddTail ((struct List *)&KeyTables, &kt->node);

    return (kt);
} /* new_keytable */



/*****************************************************************************

    NAME
	exit_keytables

    PARAMETER
	void

    RESULT
	-/-

    RETURN
	void

    DESCRIPTION
	exit function for keytables

    NOTES
	useful only, if we are using multiple keytables

	THIS is an __AUTOEXIT - function !!!!

    BUGS

    EXAMPLES

    SEE ALSO

    INTERNALS

    HISTORY
	27 Jan 1993 b_null created

******************************************************************************/

void exit_keytables (void)
{
    KEYTABLE * kt;

/* PutStr ("key_exiting\n"); */
    while (kt = GetHead (&KeyTables)) {
	delete_keytable (kt, 1);
    } /* while */
/* PutStr ("key_exited\n"); */
} /* exit_keytables */





/******************************************************************************
*****  ENDE keytables.c
******************************************************************************/
