/*
 * keys.c: Decides what happens when you press a key
 *
 * Written By Michael Sandrof
 * Copyright(c) 1990 Michael Sandrof
 * See the COPYRIGHT file, or do a HELP IRCII COPYRIGHT 
 */

#include "irc.h"
#include "config.h"
#include "commands.h"
#include "history.h"
#include "ircaux.h"
#include "input.h"
#include "keys.h"
#include "names.h"
#include "output.h"
#include "screen.h"
#include "term.h"
#include "vars.h"
#include "window.h"

static void new_key (unsigned, unsigned, int, int, char *);

/* 
 * This is it.. this is the keymap -- replaces all those thousands of
 * lines that used to be at the end of this file.... good riddens, too.
 */
KeyTable *keys[NUM_KEYMAPS];

/*
 * init_keys_1 - initializes all the key bindings to zero and calls
 *    init_keys_2 which does all the default bindings
 */
void init_keys_1 (void)
{
static	void 	init_keys_2 (void);
	int 	i;

	for (i = 0; i <= MAX_META; i++)
		keys[i] = NULL;

	init_keys_2();
}
 
/*
 * make_new_metamap -- initializes a new metakey map, of 256 bindings.
 * If you dont use a metamap, it never gets created, saving space.
 */
static void 	make_new_metamap (int which)
{
	int j;

	if (keys[which])
		panic("metamap already exists");

	keys[which] = new_malloc(sizeof(KeyTable));

	for (j = 0; j <= 255; j++)
		(*keys[which])[j] = NULL;
}

static void 	remove_metamap (int i)
{
	int j;

	for (j = 0; j <= 255; j++)
		if ((*keys[i])[j])
			panic("Metamap %d is not empty", i);

	new_free((char **)&keys[i]);
}

/*
 * lookup_function: looks up an irc function by name, and returns the
 * number of functions that match the name, and sets where index points
 * to to be the index of the (first) function found.
 */
int lookup_function (char *name, int *lf_index)
{
	int	len,
		cnt,
		i;

	if (name)
	{
		upper(name);
		len = strlen(name);
		cnt = 0;
		*lf_index = -1;
		for (i = 0; i < NUMBER_OF_FUNCTIONS; i++)
		{
			if (strncmp(name, key_names[i].name, len) == 0)
			{
				cnt++;
				if (*lf_index == -1)
					*lf_index = i;
			}
		}
		if (*lf_index == -1)
			return (0);
		if (my_stricmp(name, key_names[*lf_index].name) == 0)
			return (1);
		else
			return (cnt);
	}
	return (0);
}

/*
 * display_key: converts the character c to a displayable form and returns
 * it.  Very simple indeed 
 */
unsigned char *display_key (unsigned char c)
{
	static	unsigned char key[3];

	key[2] = (char) 0;
	if (c < 32)
	{
		key[0] = '^';
		key[1] = c + 64;
	}
	else if (c == '\177')
	{
		key[0] = '^';
		key[1] = '?';
	}
	else
	{
		key[0] = c;
		key[1] = (char) 0;
	}
	return (key);
}

/*
 * show_binding: given the ascii value of a key and a meta key status (1 for
 * meta1 keys, 2 for meta2 keys, anything else for normal keys), this will
 * display the key binding for the key in a nice way
 */
void show_binding (unsigned char c, int meta)
{
	char	meta_str[8];

	*meta_str = 0;
	if (meta < 1 || meta > MAX_META)
		meta = 0;
	else
		sprintf(meta_str, "META%d-", meta);

	if (keys[meta] && (*keys[meta])[c])
		say("%s%s is bound to %s %s", meta_str, display_key(c),
			key_names[(*keys[meta])[c]->key_index].name, 
		        ((*keys[meta])[c]->stuff &&
			   (*((*keys[meta])[c]->stuff))) ? 
			      (*keys[meta])[c]->stuff : empty_string);
	else
		say("%s%s is bound to NOTHING", meta_str, display_key(c));
}

/*
 * parse_key: converts a key string. Accepts any key, or ^c where c is any
 * key (representing control characters), or META1- or META2- for meta1 or
 * meta2 keys respectively.  The string itself is converted to true ascii
 * value, thus "^A" is converted to 1, etc.  Meta key info is removed and
 * returned as the function value, 0 for no meta key, 1 for meta1, and 2 for
 * meta2.  Thus, "META1-a" is converted to "a" and a 1 is returned.
 * Furthermore, if ^X is bound to META2_CHARACTER, and "^Xa" is passed to
 * parse_key(), it is converted to "a" and 2 is returned.  Do ya understand
 * this? 
 */
int parse_key (char *key_str)
{
	char	*ptr1,
		*ptr2;
	unsigned char	c;
	int	meta = 0;

	ptr2 = ptr1 = key_str;
	while (*ptr1)
	{
		if (*ptr1 == '^')
		{
			ptr1++;
			switch (*ptr1)
			{
				case 0:
					*(ptr2++) = '^';
					break;
				case '?':
					*(ptr2++) = '\177';
					ptr1++;
					break;
				default:
					c = *(ptr1++);
					if (islower(c))
						c = toupper(c);
					if (c < 64)
					{
						say("Invalid key sequence: ^%c", c);
						return (-1);
					}
					*(ptr2++) = c - 64;
			}
		}
		else
			*(ptr2++) = *(ptr1++);
	}
	*ptr2 = (char) 0;
	if (strlen(key_str) > 1)
	{
		/* 
		 * There used to be ten cases here that checked for
		 * each of the METAX- strings.  Now we want to be able
		 * to have double digit METAXX strings, so we look after
		 * the META and extract the number there before the hyphen.
		 * then we remove the METAXX- string.
		 */
		if (my_strnicmp(key_str, "META", 4) == 0)
		{
			char *ptr = key_str+4,
			     *str = key_str+4;

			while (*ptr && *ptr != '-')
				ptr++;
			if (*ptr)
				ptr++;

			if ((meta = atoi(str)) > MAX_META)
			{
				say("META%d is out of range", meta);
				return -1;
			}
			strcpy(key_str, ptr);
		}
		/* 
		 * Here too, used to be ten cases.  Im being a little more
		 * risky by doing it this way, becuase it makes the assumption
		 * that METAX_CHARACTERs are all defined as consecutive
		 * numbers.  A note has gone in keys.h.proto to not break
		 * up the METAX_CHARACTER defines.
		 */
		else
		{
			char *safe_copy;
			int foo = META1_CHARACTER - 1; /* just to make sure */

			if ((*keys[0])[(u_char)*key_str])
				foo = (*keys[0])[(u_char) *key_str]->key_index;
			if ((foo >= META1_CHARACTER) && (foo - META1_CHARACTER < MAX_META))
				meta = foo - META1_CHARACTER + 1;
			safe_copy = (char *)alloca(strlen(key_str));
			strcpy(safe_copy, key_str + 1);
			strcpy(key_str, safe_copy);
		}
	}
	return (meta);
}

/*
 * bind_it: does the actually binding of the function to the key with the
 * given meta modifier
 */
static void bind_it (char *function, char *string, unsigned char key, int meta)
{
	int	cnt,
		bi_index,
		i;

	if (meta < 1 || meta > MAX_META)
		meta = 0;

	if (*string == (char) 0)
		string = (char *) 0;

	switch (cnt = lookup_function(function, &bi_index))
	{
		case 0:
			say("No such function: %s", function);
			break;
		case 1:
		{
			new_key (meta, key, bi_index, 1, string);
			show_binding(key, meta);
			break;
		}
		default:
			say("Ambiguous function name: %s", function);
			for (i = 0; i < cnt; i++, bi_index++)
				put_it("%s", key_names[bi_index].name);
			break;
	}
}

/* parsekeycmd: does the PARSEKEY command.  */
BUILT_IN_COMMAND(parsekeycmd)
{
	int	i;
	char	*arg;

	if ((arg = next_arg(args, &args)) != NULL)
	{
		switch (lookup_function(arg, &i))
		{
			case 0:
				say("No such function %s", arg);
				return;
			case 1:
				key_names[i].func(0, args);
				break;
			default:
				say("Ambigious function %s", arg);
				break;
		}
	}
}

/*
 * bindcmd: the bind command, takes a key sequence followed by a function
 * name followed by option arguments (used depending on the function) and
 * binds a key.  If no function is specified, the current binding for that
 * key is shown 
 */
BUILT_IN_COMMAND(bindcmd)
{
	unsigned char	*key,
			*function;
	int		meta;

	if ((key = new_next_arg(args, &args)) != NULL)
	{
		if ((meta = parse_key(key)) == -1)
			return;

		if (strlen(key) > 1)
		{
			say("Key sequences may not contain more than two keys");
			return;
		}

		if ((function = next_arg(args, &args)) != NULL)
			bind_it(function, args, *key, meta);
		else if (*key)
			show_binding(*key, meta);
		/*
		 * Allow /bind meta4 to show all the meta4 bindings.
		 */
		else
		{
			int j, k = charset_size();
			if (keys[meta])
			{
				for (j = 0; j < k; j++)
					if ((*keys[meta])[j] && ((*keys[meta])[j]->key_index != NOTHING) && ((*keys[meta])[j]->key_index != SELF_INSERT))
						show_binding(j, meta);
			}
			else
				say("No META%d bindings", meta);
		}
	}
	else
	{
		int i, j, k = charset_size();
		for (i = 0; i <= MAX_META; i++)
		{
			if (!keys[i])
				continue;

			for (j = 0; j < k; j++)
				if ((*keys[i])[j] && ((*keys[i])[j]->key_index != NOTHING) && ((*keys[i])[j]->key_index != SELF_INSERT))
					show_binding(j, i);
		}
	}
}

/*
 * rbindcmd: does the rbind command.  you give it a string that something
 * is bound to and it tells you all the things that are bound to that
 * functions
 */
BUILT_IN_COMMAND(rbindcmd)
{
	int	f;
	char	*arg;

	if ((arg = next_arg(args, &args)) != NULL)
	{
		int	i, j;
		int	charsize = charset_size();

		switch (lookup_function(arg, &f))
		{
			case 0:
				say("No such function %s", arg);
				return;

			case 1:
				break;

			default:
				say("Ambigious function %s", arg);
				return;
		}

		for (i = 0; i <= MAX_META; i++)
		{
			if (!keys[i])
				continue;

			for (j = 0; j < charsize; j++)
			    if ((*keys[i])[j] && (*keys[i])[j]->key_index == f)
				show_binding(j, i);
		}
	}
}

/*
 * type: The TYPE command.  This parses the given string and treats each
 * character as tho it were typed in by the user.  Thus key bindings are used
 * for each character parsed.  Special case characters are control character
 * sequences, specified by a ^ follow by a legal control key.  Thus doing
 * "/TYPE ^B" will be as tho ^B were hit at the keyboard, probably moving the
 * cursor backward one character.
 */
BUILT_IN_COMMAND(type)
{
	int	c;
	char	key;

	while (*args)
	{
		if (*args == '^')
		{
			switch (*(++args))
			{
			case '?':
				key = '\177';
				args++;
				break;
			default:
				c = *(args++);
				if (islower(c))
					c = toupper(c);
				if (c < 64)
				{
					say("Invalid key sequence: ^%c", c);
					return;
				}
				key = c - 64;
				break;
			}
		}
		else if (*args == '\\')
		{
			key = *++args;
			args++;
		}
		else
			key = *(args++);
		edit_char(key);
	}
}

KeyMapNames key_names[] =
{
	{ "BACKSPACE",			input_backspace },
	{ "BACKWARD_CHARACTER",		backward_character },
	{ "BACKWARD_HISTORY",		backward_history },
	{ "BACKWARD_WORD",		input_backward_word },
	{ "BEGINNING_OF_LINE",		input_beginning_of_line },
	{ "BLINK",			insert_blink },
	{ "BOLD",			insert_bold },
	{ "CLEAR_SCREEN",		clear_screen },
	{ "COMMAND_COMPLETION",		command_completion },
	{ "CPU_SAVER",			cpu_saver_on },
	{ "DELETE_CHARACTER",		input_delete_character },
	{ "DELETE_NEXT_WORD",		input_delete_next_word },
	{ "DELETE_PREVIOUS_WORD",	input_delete_previous_word },
	{ "DELETE_TO_PREVIOUS_SPACE",	input_delete_to_previous_space },
	{ "END_OF_LINE",		input_end_of_line },
	{ "ERASE_LINE",			input_clear_line },
	{ "ERASE_TO_BEG_OF_LINE",	input_clear_to_bol },
	{ "ERASE_TO_END_OF_LINE",	input_clear_to_eol },
	{ "FORWARD_CHARACTER",		forward_character },
	{ "FORWARD_HISTORY",		forward_history },
	{ "FORWARD_WORD",		input_forward_word },
	{ "HIGHLIGHT_OFF",		highlight_off },
	{ "META1_CHARACTER",		meta1_char },
	{ "META2_CHARACTER",		meta2_char },
	{ "META3_CHARACTER",		meta3_char },
	{ "META4_CHARACTER",		meta4_char },
	{ "META5_CHARACTER",		meta5_char },
	{ "META6_CHARACTER",		meta6_char },
	{ "META7_CHARACTER",		meta7_char },
	{ "META8_CHARACTER",		meta8_char },
	{ "META9_CHARACTER",		meta9_char },
	{ "META10_CHARACTER",		meta10_char },
	{ "META11_CHARACTER",		meta11_char },
	{ "META12_CHARACTER",		meta12_char },
	{ "META13_CHARACTER",		meta13_char },
	{ "META14_CHARACTER",		meta14_char },
	{ "META15_CHARACTER",		meta15_char },
	{ "META16_CHARACTER",		meta16_char },
	{ "META17_CHARACTER",		meta17_char },
	{ "META18_CHARACTER",		meta18_char },
	{ "META19_CHARACTER",		meta19_char },
	{ "META20_CHARACTER",		meta20_char },
	{ "META21_CHARACTER",		meta21_char },
	{ "META22_CHARACTER",		meta22_char },
	{ "META23_CHARACTER",		meta23_char },
	{ "META24_CHARACTER",		meta24_char },
	{ "META25_CHARACTER",		meta25_char },
	{ "META26_CHARACTER",		meta26_char },
	{ "META27_CHARACTER",		meta27_char },
	{ "META28_CHARACTER",		meta28_char },
	{ "META29_CHARACTER",		meta29_char },
	{ "META30_CHARACTER",		meta30_char },
	{ "META31_CHARACTER",		meta31_char },
	{ "META32_CHARACTER",		meta32_char },
	{ "META33_CHARACTER",		meta33_char },
	{ "META34_CHARACTER",		meta34_char },
	{ "META35_CHARACTER",		meta35_char },
	{ "META36_CHARACTER",		meta36_char },
	{ "META37_CHARACTER",		meta37_char },
	{ "META38_CHARACTER",		meta38_char },
	{ "META39_CHARACTER",		meta39_char },
	{ "NEXT_WINDOW",		next_window },
	{ "NOTHING",			NULL },
	{ "PARSE_COMMAND",		parse_text },
	{ "PREVIOUS_WINDOW",		previous_window },
	{ "QUIT_IRC",			irc_quit },
	{ "QUOTE_CHARACTER",		quote_char },
	{ "REFRESH_INPUTLINE",		refresh_inputline },
	{ "REFRESH_SCREEN",		(KeyBinding) refresh_screen },
	{ "REVERSE",			insert_reverse },
	{ "SCROLL_BACKWARD",		scrollback_backwards },
	{ "SCROLL_END",			scrollback_end },
	{ "SCROLL_FORWARD",		scrollback_forwards },
	{ "SCROLL_START",		scrollback_start },
	{ "SELF_INSERT",		input_add_character },
	{ "SEND_LINE",			get_line_return },
	{ "SHOVE_TO_HISTORY",		shove_to_history },
	{ "STOP_IRC",			term_pause },
	{ "SWAP_LAST_WINDOW",		swap_last_window },
	{ "SWAP_NEXT_WINDOW",		swap_next_window },
	{ "SWAP_PREVIOUS_WINDOW",	swap_previous_window },
	{ "SWITCH_CHANNELS",		switch_channels },
	{ "TOGGLE_INSERT_MODE",		toggle_insert_mode },
	{ "TOGGLE_STOP_SCREEN",		toggle_stop_screen },
	{ "TRANSPOSE_CHARACTERS",	input_transpose_characters },
	{ "TYPE_TEXT",			type_text },
	{ "UNCLEAR_SCREEN",		input_unclear_screen },
	{ "UNDERLINE",			insert_underline },
	{ "UNSTOP_ALL_WINDOWS",		unstop_all_windows },
	{ "YANK_FROM_CUTBUFFER",	input_yank_cut_buffer }
};

/* real simple way to make a new keybinding */
static void new_key (unsigned map, unsigned chr, int type, int change, char *stuff)
{
	/*
	 * Create a map first time we bind into it.  We have to do this
	 * Because its possible to do /bind METAX-f when there is not
	 * otherwise any key bound to METAX.
	 */
	if (!keys[map])
		make_new_metamap(map);

	if ((*keys[map])[chr])
	{
		if ((*keys[map])[chr]->stuff)
			new_free((char **)&(*keys[map])[chr]->stuff);
		if ((*keys[map])[chr]->filename)
			new_free((char **)&(*keys[map])[chr]->filename);
		new_free((char **)&((*keys[map])[chr]));
		(*keys[map])[chr] = NULL;
	}

	if (type != NOTHING)
	{
		(*keys[map])[chr] = (KeyMap *)new_malloc(sizeof(KeyMap));
		(*keys[map])[chr]->key_index = type;
		(*keys[map])[chr]->changed = change;
		(*keys[map])[chr]->filename = m_strdup(current_package());
		if (stuff)
			(*keys[map])[chr]->stuff = m_strdup(stuff);
		else
			(*keys[map])[chr]->stuff = NULL;
	}
}

/* special interface to new_key for the default key bindings */
static void snew_key (unsigned map, unsigned chr, int type)
{
	new_key (map, chr, type, 0, NULL);
}

/* 
 * This is where you put all the default key bindings.  This is a lot
 * simpler, just defining those you need, instead of all of them, isnt
 * it?  And it takes up so much less memory, too...
 */
static	void 	init_keys_2 (void)
{
	int i;

	/* all the "default" bindings are self_insert unless we bind
	 * them differently */
	for (i = 0; i <= 255; i++)
		snew_key(0, i, SELF_INSERT);

	/* "default" characters that arent self_insert */
	snew_key(0,  1, BEGINNING_OF_LINE);		/* ^A */
	snew_key(0,  2, BOLD);				/* ^B */
	snew_key(0,  4, DELETE_CHARACTER);		/* ^D */
	snew_key(0,  5, END_OF_LINE);			/* ^E */
	snew_key(0,  6, BLINK);				/* ^F */
	snew_key(0,  8, BACKSPACE);			/* ^H (delete) */

	snew_key(0,  9, TOGGLE_INSERT_MODE);		/* ^I (tab) */
	snew_key(0, 10, SEND_LINE);			/* ^J (enter) */
	snew_key(0, 11, ERASE_TO_END_OF_LINE);		/* ^K */
	snew_key(0, 12, REFRESH_SCREEN);		/* ^L (linefeed) */
	snew_key(0, 13, SEND_LINE);			/* ^M (return) */
	snew_key(0, 14, FORWARD_HISTORY);		/* ^N */
	snew_key(0, 15, HIGHLIGHT_OFF);			/* ^O */
	snew_key(0, 16, BACKWARD_HISTORY);		/* ^P */

	snew_key(0, 17, QUOTE_CHARACTER);		/* ^Q */
	snew_key(0, 19, TOGGLE_STOP_SCREEN);		/* ^S */
	snew_key(0, 20, TRANSPOSE_CHARACTERS);		/* ^T */
	snew_key(0, 21, ERASE_LINE);			/* ^U */
	snew_key(0, 22, REVERSE);			/* ^V */
	snew_key(0, 23, NEXT_WINDOW);			/* ^W */
	snew_key(0, 24, META2_CHARACTER);		/* ^X */

	snew_key(0, 25, YANK_FROM_CUTBUFFER);		/* ^Y */
	snew_key(0, 26, STOP_IRC);			/* ^Z */
	snew_key(0, 27, META1_CHARACTER);		/* ^[ (escape) */
	snew_key(0, 29, SHOVE_TO_HISTORY);		/* ^] */
	snew_key(0, 31, UNDERLINE);			/* ^_ */

	snew_key(0, 127, BACKSPACE);			/* ^? (delete) */

	/* 
	 * european keyboards (and probably others) use the eigth bit
	 * for extended characters.  Having these keys bound by default
	 * causes them lots of grief, so unless you really want to use
	 * these, they are commented out.
	 */
#ifdef EMACS_KEYBINDS
	snew_key(0, 188, SCROLL_START);	
	snew_key(0, 190, SCROLL_END);
	snew_key(0, 226, BACKWARD_WORD);
	snew_key(0, 228, DELETE_NEXT_WORD);
	snew_key(0, 229, SCROLL_END);
	snew_key(0, 230, FORWARD_WORD);
	snew_key(0, 232, DELETE_PREVIOUS_WORD);
	snew_key(0, 255, DELETE_PREVIOUS_WORD);
#endif

	/* meta 1 characters */
	snew_key(1,  27, COMMAND_COMPLETION);
	snew_key(1,  46, CLEAR_SCREEN);
	snew_key(1,  60, SCROLL_START);
	snew_key(1,  62, SCROLL_END);
	snew_key(1,  79, META2_CHARACTER);
	snew_key(1,  91, META2_CHARACTER);
	snew_key(1,  98, BACKWARD_WORD);
	snew_key(1, 100, DELETE_NEXT_WORD);
	snew_key(1, 101, SCROLL_END);
	snew_key(1, 102, FORWARD_WORD);
	snew_key(1, 104, DELETE_PREVIOUS_WORD);
	snew_key(1, 110, SCROLL_FORWARD);
	snew_key(1, 112, SCROLL_BACKWARD);
	snew_key(1, 127, DELETE_PREVIOUS_WORD);


	/* meta 2 characters */
	snew_key(2,  26, STOP_IRC);
	snew_key(2,  65, BACKWARD_HISTORY);
	snew_key(2,  66, FORWARD_HISTORY);
	snew_key(2,  67, FORWARD_CHARACTER);
	snew_key(2,  68, BACKWARD_CHARACTER);
	snew_key(2, 110, NEXT_WINDOW);
	snew_key(2, 112, PREVIOUS_WINDOW);
	snew_key(2, '1', META32_CHARACTER);	/* home */
	snew_key(2, '4', META33_CHARACTER),	/* end */
	snew_key(2, '5', META30_CHARACTER);	/* page up */
	snew_key(2, '6', META31_CHARACTER);	/* page down */

	/* meta 3 characters */
	/* <none> */

	/* meta 4 characters -- vi key mappings */
	snew_key(4,   8, BACKWARD_CHARACTER);
	snew_key(4,  32, FORWARD_CHARACTER);
	snew_key(4,  65, META4_CHARACTER);
	snew_key(4,  72, BACKWARD_CHARACTER);
	snew_key(4,  73, META4_CHARACTER);
	snew_key(4,  74, FORWARD_HISTORY);
	snew_key(4,  75, BACKWARD_HISTORY);
	snew_key(4,  76, FORWARD_CHARACTER);
	snew_key(4,  88, DELETE_CHARACTER);
	snew_key(4,  97, META4_CHARACTER);
	snew_key(4, 104, BACKWARD_CHARACTER);
	snew_key(4, 105, META4_CHARACTER);
	snew_key(4, 106, FORWARD_HISTORY);
	snew_key(4, 107, BACKWARD_HISTORY);
	snew_key(4, 108, FORWARD_CHARACTER);
	snew_key(4, 120, DELETE_CHARACTER);

	/* I used 30-something to keep them out of others' way */
	snew_key(30, '~', SCROLL_BACKWARD);
	snew_key(31, '~', SCROLL_FORWARD);
	snew_key(32, '~', SCROLL_START);
	snew_key(33, '~', SCROLL_END);
}


/*
 * write_binding: This will write to the given FILE pointer the information
 * about the specified key binding.  The format it writes it out is such that
 * it can be parsed back in later using LOAD or with the -l switch 
 */
static void write_binding (unsigned char c, unsigned char meta, FILE *fp, int do_all)
{
	char	meta_str[8];

	if (c == 32)
		return;

	*meta_str = 0;
	if (meta < 1 || meta > MAX_META)
		meta = 0;
	else
		sprintf(meta_str, "META%d-", meta);

	if (keys[meta] && (*keys[meta])[c] && (*keys[meta])[c]->changed)
	{
		fprintf(fp, "BIND %s%s %s", meta_str, display_key(c), key_names[(*keys[meta])[c]->key_index].name);
		if ((*keys[meta])[c]->stuff && (*((*keys[meta])[c]->stuff)))
			fprintf(fp, " %s", (*keys[meta])[c]->stuff);
		fprintf(fp, "\n");
	}
}

/*
 * save_bindings: this writes all the keys bindings for IRCII to the given
 * FILE pointer using the write_binding function 
 */
void 	save_bindings (FILE *fp, int do_all)
{
	int	i, j;
	int	charsize = charset_size();

	for (i = 0; i <= MAX_META; i++)
		for (j = 0; j < charsize; j++)
			write_binding(j, i, fp, do_all);
}

void 	remove_bindings (void)
{
	int i, j;

	for (i = 0; i <= MAX_META; i++)
	{
		for (j = 0; j <= 255; j++)
			snew_key(i, j, NOTHING);
		remove_metamap(i);
	}
}

void	unload_bindings (const char *filename)
{
	int 	i, j;

	for (i = 0; i <= MAX_META; i++)
	{
		if (!keys[i])
			continue;

		for (j = 0; j < 256; j++)
		    if ((*keys[i])[j] && !strcmp((*keys[i])[j]->filename, filename))
			snew_key(i, j, NOTHING);
	}
}
