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

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#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 "ircterm.h"
#include "vars.h"
#include "window.h"
#include "tcommand.h"

extern void get_line_return(char, char *);

/* !!!DONT!!! change this number on a whim!  You could break stuff.  It
 * is defined here so that if/when we want to add more key maps, it will
 * be real easy to change all the stuff that is dependant on how many
 * key maps there are.  However, there is no support for more then 10
 * keymaps in the code!  You have been warned!
 */

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

/* 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.
 */
KeyMap *keys[NUM_KEYMAPS][256 + 1];

/*
 * 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)
{
    memset(keys, 0, sizeof(KeyMap) * NUM_KEYMAPS * 256);
    init_keys_2();
}

/*
 * 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][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_str);
    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("Illegal 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++;
	    meta = atoi(str);
	    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 {
	    int foo = META1_CHARACTER - 1;	/* just to make sure */

#ifdef __GNUC__
	    char *safe_copy;
#endif
	    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;
#ifndef __GNUC__
	    strcpy(key_str, key_str + 1);
#else
	    safe_copy = (char *) alloca(strlen(key_str));
	    strcpy(safe_copy, key_str + 1);
	    strcpy(key_str, safe_copy);
#endif
	}
    }
    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, char key, int meta)
{
    int cnt, bi_index, i;
    int changed;

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

    if (!string || !*string)
	string = NULL;

    switch (cnt = lookup_function(function, &bi_index)) {
    case 0:
	say("No such function: %s", function);
	break;
    case 1:
	{
	    changed = 1;
	    new_key(meta, key, bi_index, changed, 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.  */
void cmd_parsekey(struct command *cmd, char *args)
{
    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 
 */
void cmd_bind(struct command *cmd, char *args)
{
    char *key, *function;
    int meta = 0;

    if ((key = new_next_arg(args, &args)) != NULL) {
	int in_space = 0;

	in_space = stristr(key, "space") ? 1 : 0;
	if (((meta = parse_key(key)) == -1))
	    return;
	if (strlen(key) > 1 && !in_space) {
	    say("Key sequences may not contain more than two keys");
	    return;
	}
	if ((function = next_arg(args, &args)) != NULL)
	    bind_it(function, args, in_space ? ' ' : *key, meta);
	else
	    show_binding(in_space ? ' ' : (unsigned char) *key, meta);
    } else {
	int i, j, k = charset_size();

	for (i = 0; i <= MAX_META; i++)
	    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
 */
void cmd_rbind(struct command *cmd, char *args)
{
    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++)
	    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.
 */
void cmd_type(struct command *cmd, char *args)
{
    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("Illegal key sequence: ^%c", c);
		    return;
		}
		key = c - 64;
		break;
	    }
	} else if (*args == '\\') {
	    key = *++args;
	    args++;
	} else
	    key = *(args++);
	edit_char(key);
    }
}

extern void dcc_stats(char *);
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},
    {"BOLD", insert_bold},
    {"CHANGE_TO_SPLIT", change_to_split},
    {"CLEAR_SCREEN", clear_screen},
    {"COMMAND_COMPLETION", command_completion},
    {"DCC_PLIST", dcc_plist},
    {"DELETE_CHARACTER", input_delete_character},
    {"DELETE_NEXT_WORD", input_delete_next_word},
    {"DELETE_PREVIOUS_WORD", input_delete_previous_word},
    {"DELETE_TO_PREVIOUS_WORD", 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},
    {"IGNORE_NICK", ignore_last_nick},
    {"JOIN_LAST_INVITE", join_last_invite},
    {"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},
    {"NEW_BEGINNING_OF_LINE", new_input_beginning_of_line},
    {"NEW_SCROLL_BACKWARD", my_scrollback},
    {"NEW_SCROLL_END", my_scrollend},
    {"NEW_SCROLL_FORWARD", my_scrollforward},
    {"NEXT_WINDOW", next_window},
    {"NICK_COMPLETION", nick_completion},
    {"NOTHING", NULL},
    {"PARSE_COMMAND", parse_text},
    {"PREVIOUS_WINDOW", previous_window},
    {"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", (void (*)(char, char *)) term_pause},
    {"SWAP_LAST_WINDOW", swap_last_window},
    {"SWAP_NEXT_WINDOW", swap_next_window},
    {"SWAP_PREVIOUS_WINDOW", swap_previous_window},
    {"SWITCH_CHANNELS", switch_channels},
    {"TAB_MSG", input_msgreply},
    {"TOGGLE_INSERT_MODE", toggle_insert_mode},
    {"TOGGLE_STOP_SCREEN", toggle_stop_screen},
    {"TRANSPOSE_CHARACTERS", input_transpose_characters},
    {"TYPE_TEXT", type_text},
    {"UNDERLINE", insert_underline},
    {"UNSTOP_ALL_WINDOWS", unstop_all_windows},
    {"WHOLEFT", wholeft},

    {"WINDOW_BALANCE", window_key_balance},
    {"WINDOW_GROW_ONE", window_grow_one},
    {"WINDOW_HIDE", window_key_hide},
    {"WINDOW_KILL", window_key_kill},
    {"WINDOW_LIST", window_key_list},
    {"WINDOW_MOVE", window_key_move},
    {"WINDOW_SHRINK_ONE", window_shrink_one},

    {"WINDOW_SWAP_1", window_swap1},
    {"WINDOW_SWAP_2", window_swap2},
    {"WINDOW_SWAP_3", window_swap3},
    {"WINDOW_SWAP_4", window_swap4},
    {"WINDOW_SWAP_5", window_swap5},
    {"WINDOW_SWAP_6", window_swap6},
    {"WINDOW_SWAP_7", window_swap7},
    {"WINDOW_SWAP_8", window_swap8},
    {"WINDOW_SWAP_9", window_swap9},
    {"WINDOW_SWAP_10", window_swap10},
    {"YANK_FROM_CUTBUFFER", input_yank_cut_buffer}
};

/* real simple way to make a new keybinding */
void new_key(int map, int chr, int type, int change, char *stuff)
{
    if (keys[map][chr]) {
	if (keys[map][chr]->stuff)
	    new_free(&(keys[map][chr]->stuff));
	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;
	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(int map, int 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 <= 254; 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, WHOLEFT);	/* ^F */
    snew_key(0, 8, BACKSPACE);	/* ^H (delete) */
#if 0
    snew_key(0, 9, TOGGLE_INSERT_MODE);	/* ^I (tab) */
#endif
    snew_key(0, 9, TAB_MSG);	/* ^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, QUOTE_CHARACTER);	/* ^N */
    snew_key(0, 15, IGNORE_NICK);	/* ^O */
    snew_key(0, 16, BACKWARD_HISTORY);	/* ^P */

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

    snew_key(0, 25, YANK_FROM_CUTBUFFER);	/* ^Y */

    snew_key(0, 26, STOP_IRC);	/* ^Z */
    snew_key(0, 27, META1_CHARACTER);	/* ^[ (escape) */
#if 0
    snew_key(0, 29, SHOVE_TO_HISTORY);	/* ^] */
#endif
    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. */

    /* 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);

    snew_key(1, '1', WINDOW_SWAP_1);
    snew_key(1, '2', WINDOW_SWAP_2);
    snew_key(1, '3', WINDOW_SWAP_3);
    snew_key(1, '4', WINDOW_SWAP_4);
    snew_key(1, '5', WINDOW_SWAP_5);
    snew_key(1, '6', WINDOW_SWAP_6);
    snew_key(1, '7', WINDOW_SWAP_7);
    snew_key(1, '8', WINDOW_SWAP_8);
    snew_key(1, '9', WINDOW_SWAP_9);
    snew_key(1, '0', WINDOW_SWAP_10);

    /* meta 2 characters */
#ifdef ALLOW_STOP_IRC
    snew_key(2, 26, STOP_IRC);
#endif
    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, SWAP_NEXT_WINDOW);
    snew_key(2, 112, PREVIOUS_WINDOW);
/* These are added for BSD console mode */

    snew_key(2, 'H', NEW_BEGINNING_OF_LINE);
    snew_key(2, 'F', NEW_SCROLL_END);
    snew_key(2, 'I', NEW_SCROLL_BACKWARD);
    snew_key(2, 'G', NEW_SCROLL_FORWARD);

    snew_key(2, '+', WINDOW_GROW_ONE);
    snew_key(2, '-', WINDOW_SHRINK_ONE);
    snew_key(2, 'm', WINDOW_MOVE);
    snew_key(2, 'l', WINDOW_LISTK);
    snew_key(2, 'k', WINDOW_KILL);
    snew_key(2, 'b', WINDOW_BALANCE);
    snew_key(2, 'h', WINDOW_HIDE);

    /* meta 3 characters */
    snew_key(2, '[', META3_CHARACTER);
    snew_key(2, '1', META3_CHARACTER);

    snew_key(2, 'Q', DCC_PLIST);
/* end of add */

    snew_key(2, '1', NEW_BEGINNING_OF_LINE);
    snew_key(2, '4', NEW_SCROLL_END);
    snew_key(2, '5', NEW_SCROLL_BACKWARD);
    snew_key(2, '6', NEW_SCROLL_FORWARD);

    snew_key(2, '+', WINDOW_GROW_ONE);
    snew_key(2, '-', WINDOW_SHRINK_ONE);
    snew_key(2, 'm', WINDOW_MOVE);
    snew_key(2, 'l', WINDOW_LISTK);
    snew_key(2, 'k', WINDOW_KILL);
    snew_key(2, 'b', WINDOW_BALANCE);
    snew_key(2, 'h', WINDOW_HIDE);

    /* meta 3 characters */
    /* <none> */
    snew_key(2, '[', META3_CHARACTER);
    snew_key(2, '1', META3_CHARACTER);

    snew_key(3, 'E', DCC_PLIST);

    /* 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);
}

/*
 * 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][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 clear_bindings(void)
{
    int i, j;
    int charsize = charset_size();

    for (i = 0; i <= MAX_META; i++)
	for (j = 0; j < charsize; j++) {
	    if (keys[i][j] && keys[i][j]->stuff)
		new_free(&(keys[i][j]->stuff));
	    if (keys[i][j])
		new_free((char **) &(keys[i][j]));
	}
}


syntax highlighted by Code2HTML, v. 0.9.1