/*1* keys.c: Keeps track of what happens whe you press a key.2*3* Written By Michael Sandrof4* Copyright(c) 1990 Michael Sandrof5* See the COPYRIGHT file, or do a HELP IRCII COPYRIGHT6*7* Substantial re-implementation by Jeremy Nelson8* Copyright 1998 EPIC Software Labs9* See the COPYRIGHT file, or do a HELP IRCII COPYRIGHT10*/1112#include "irc.h"13static char cvsrevision[] = "$Id: keys.c 3 2008-02-25 09:49:14Z keaston $";14CVS_REVISION(keys_c)15#include "struct.h"16#include "config.h"17#include "commands.h"18#include "history.h"19#include "ircaux.h"20#include "input.h"21#include "keys.h"22#include "names.h"23#include "output.h"24#include "screen.h"25#include "ircterm.h"26#include "vars.h"27#include "window.h"28#define MAIN_SOURCE29#include "modval.h"3031#define KEY(meta, ch) (*keys[meta])[ch]3233typedef unsigned char uc;34static void new_key (int, unsigned, int, int, char *);35static void snew_key (int meta, unsigned chr, char *what);36static uc * display_key (uc c);37static int lookup_function (const uc *name, int *lf_index);38static int parse_key (const uc *sequence, uc *term);3940#ifdef GUI41char *mouse_actions[] =42{43"RCLICK",44"STATUSRCLICK",45"NICKLISTRCLICK",46"LCLICK",47"STATUSLCLICK",48"NICKLISTLCLICK",49"MCLICK",50"STATUSMCLICK",51"NICKLISTMCLICK",52"RDBLCLICK",53"STATUSRDBLCLICK",54"NICKLISTRDBLCLICK",55"LDBLCLICK",56"STATUSLDBLCLICK",57"NICKLISTLDBLCLICK",58"MDBLCLICK",59"STATUSMDBLCLICK",60"NICKLISTMDBLCLICK"61};62#endif63646566/*67* Yet again we've changed how the key maps are held. This time, hopefully68* its the second to last time, as we've made the entire things independant69* of the number of meta keymaps that are available (it can change.) The70* only thing i see left to be done is to encapsulate all this data inside71* a class so that different contexts can have different bindings sets.72* I'm sure that will come up some day. But anyhow, on to the details!73*/7475/*76* The actual "stuff" that is in a keybinding is defined in keys.h.77* At the time i write this, its just an int, two pointers, and a char,78* so its 16 bytes (after padding)....79*80* typedef struct81* {82* int key_index;83* char *stuff;84* char *filename;85* char changed;86* } KeyMap;87*88* Some special notes about 'key_index'. No longer are the meta bindings89* just normal bindings -- having them hardcoded as normal bindings limited90* the ability of the user to set the number of meta states that they wanted.91* Now all the negative values for 'key_index' are reserved for those meta92* states. If something is bound to META2_CHARACTER, then 'key_index' is93* given the value -2. Otherwise, 'key_index' is given the value of the94* entry of the binding in the 'key_names' table. Note that 'key_index' is95* not strictly sorted. The first two bindings are special and are hardcoded,96* and you must not change them. Entry 0 must always be "NOTHING", and97* entry 1 must always be "SELF_INSERT". The enum that was in keys.h98* is now totaly obsolete -- we no longer use the symbolic names, but instead99* always use the full string name for the binding. This makes it much100* easier to add new key bindings, as there is only one place to manage.101*102*103* Each meta map is a collection of 256 bindings...104*105* typedef KeyMap MetaMap[256];106*107* But since most of the meta maps are either sparse or empty, it makes very108* little sense to actually allocate 4k (16 * 256) for each of the maps only109* to have them unused. So instead the meta map is just 256 pointers to110* these objects, which then are allocated dynamically.111*112* typedef KeyMap *MetaMap[256]; (better)113*114* That means that the overhead for a completely empty meta map is 1k, and115* the map needs to be 75% full before it uses more memory than it would have116* otherwise. So its a reasonable win.117*118* Now we need to have the meta maps stored somehow. Originally, we used119* to just make an array of them...120*121* typedef MetaMap KeyTable[MAX_META];122*123* but then again, many of those MetaMaps are going to be totaly empty.124* Why should we allocate 1k to something that isnt going to be used?125* So instead we should keep pointers to the maps and allocate them as126* neccesary at runtime...127*128* typedef MetaMap *KeyTable[MAX_META]; (better)129*130* Which is what we had before. This works out fine, except, that the131* number of meta maps is hardcoded into the client at compile time.132* Wouldn't it be nice to be able to determine at runtime how many maps133* we want and be able to change them as neccesary? We can do this by134* having a pointer to the set of pointers of MetaMaps...135*136* typedef MetaMap **KeyTable; (dyanmic now)137*138* And so we dynamically allocate to MetaSet enough pointers to hold the139* number of MetaMap's we want. Then any time we want to use a MetaMap,140* we allocate space for it. Then any time we want to use a binding in the141* MetaMap, we allocate space for it and set its pointer.142*143* So the final dereference for a specific key binding 'X' in meta map 'Y' is:144*145* KeyTable keys;146* keys[Y] The pointer to the 'Y'th MetaMap147* (*keys[Y]) The MetaMap itself148* (*keys[Y])[X] The pointer to the 'X'th character in the149* 'Y'th MetaMap150* (*keys[Y])[X]->key_index151* What 'Y,X' is actually bound to. This is a152* negative value for a transition to a new153* meta state Y, positive for a terminating154* binding.155*/156157/* * * * * * * * * * * * * * * METAMAP MANAGEMENT * * * * * * * * * * * */158/*159* This is where all the bindings are stored160* Also we store how big it thinks it is, and how big it actually is.161*/162163/* KeyMapNames: the structure of the keymap to realname array */164typedef struct165{166char * name;167KeyBinding func;168} KeyMapNames;169static KeyMapNames key_names[] =170{171/* The first two here are "magic" binds */172{ "NOTHING", NULL },173{ "SELF_INSERT", input_add_character },174{ "ALTCHARSET", insert_altcharset },175{ "AUTOREPLY", input_autoreply },176{ "AUTOREPLY_BACK", input_autoreplyback },177{ "BACKSPACE", input_backspace },178{ "BACKWARD_CHARACTER", backward_character },179{ "BACKWARD_HISTORY", backward_history },180{ "BACKWARD_WORD", input_backward_word },181{ "BEGINNING_OF_LINE", input_beginning_of_line },182{ "BLINK", insert_blink },183{ "BOLD", insert_bold },184#ifdef WANT_CDCC185{ "CDCC_PLIST", cdcc_plist },186#endif187{ "CHANNEL_CHOPS", channel_chops },188{ "CHANNEL_NONOPS", channel_nonops },189{ "CHANGE_TO_SPLIT", change_to_split },190#ifdef WANT_CHELP191{ "CHELP", do_chelp },192#endif193{ "CLEAR_SCREEN", clear_screen },194{ "COMMAND_COMPLETION", command_completion },195{ "CPU_SAVER", cpu_saver_on },196{ "DCC_PLIST", dcc_plist },197{ "DCC_STATS", dcc_ostats },198{ "DELETE_CHARACTER", input_delete_character },199{ "DELETE_NEXT_WORD", input_delete_next_word },200{ "DELETE_PREVIOUS_WORD", input_delete_previous_word },201{ "DELETE_TO_PREVIOUS_SPACE", input_delete_to_previous_space },202{ "END_OF_LINE", input_end_of_line },203#ifdef TRANSLATE204{ "ENTER_DIGRAPH", enter_digraph },205#endif206{ "ERASE_LINE", input_clear_line },207{ "ERASE_TO_BEG_OF_LINE", input_clear_to_bol },208{ "ERASE_TO_END_OF_LINE", input_clear_to_eol },209{ "FORWARD_CHARACTER", forward_character },210{ "FORWARD_HISTORY", forward_history },211{ "FORWARD_WORD", input_forward_word },212{ "HIGHLIGHT_OFF", highlight_off },213{ "IGNORE_NICK", ignore_last_nick },214{ "JOIN_LAST_INVITE", join_last_invite },215{ "NEW_BEGINNING_OF_LINE", new_input_beginning_of_line },216{ "NEW_SCROLL_BACKWARD", my_scrollback },217{ "NEW_SCROLL_END", my_scrollend },218{ "NEW_SCROLL_FORWARD", my_scrollforward},219{ "NEXT_WINDOW", BX_next_window },220{ "NICK_COMPLETION", nick_completion },221{ "PARSE_COMMAND", parse_text },222#ifdef GUI223{ "PASTE_TO_INPUT", paste_to_input },224#endif225{ "PREVIOUS_WINDOW", BX_previous_window },226{ "QUIT_IRC", irc_quit },227{ "QUOTE_CHARACTER", quote_char },228{ "REFRESH_INPUTLINE", refresh_inputline },229{ "REFRESH_SCREEN", (KeyBinding) refresh_screen },230{ "REFRESH_STATUS", (KeyBinding) BX_update_all_status },231{ "REVERSE", insert_reverse },232{ "SCROLL_BACKWARD", BX_scrollback_backwards },233{ "SCROLL_END", BX_scrollback_end },234{ "SCROLL_FORWARD", BX_scrollback_forwards },235{ "SCROLL_START", BX_scrollback_start },236{ "SEND_LINE", send_line },237{ "SHOVE_TO_HISTORY", shove_to_history },238#ifdef ALLOW_STOP_IRC239{ "STOP_IRC", term_pause },240#endif241{ "SWAP_LAST_WINDOW", BX_swap_last_window },242{ "SWAP_NEXT_WINDOW", BX_swap_next_window },243{ "SWAP_PREVIOUS_WINDOW", BX_swap_previous_window },244{ "SWITCH_CHANNELS", switch_channels },245#ifdef WANT_TABKEY246{ "TAB_COMPLETION", tab_completion },247#endif248{ "TAB_MSG", input_msgreply },249{ "TAB_MSG_BACK", input_msgreplyback },250{ "TOGGLE_CLOAK", toggle_cloak },251{ "TOGGLE_INSERT_MODE", toggle_insert_mode },252{ "TOGGLE_STOP_SCREEN", BX_toggle_stop_screen },253{ "TRANSPOSE_CHARACTERS", input_transpose_characters },254{ "TYPE_TEXT", type_text },255{ "UNCLEAR_SCREEN", input_unclear_screen },256{ "UNDERLINE", insert_underline },257{ "UNSTOP_ALL_WINDOWS", BX_unstop_all_windows },258{ "WHOLEFT", wholeft },259{ "WINDOW_BALANCE", window_key_balance },260{ "WINDOW_GROW_ONE", window_grow_one },261#ifdef WANT_CHELP262{ "WINDOW_HELP", w_help },263#endif264{ "WINDOW_HIDE", window_key_hide },265{ "WINDOW_KILL", window_key_kill },266{ "WINDOW_LIST", window_key_list },267{ "WINDOW_MOVE", window_key_move },268{ "WINDOW_SHRINK_ONE", window_shrink_one },269270{ "WINDOW_SWAP_1", window_swap1 },271{ "WINDOW_SWAP_2", window_swap2 },272{ "WINDOW_SWAP_3", window_swap3 },273{ "WINDOW_SWAP_4", window_swap4 },274{ "WINDOW_SWAP_5", window_swap5 },275{ "WINDOW_SWAP_6", window_swap6 },276{ "WINDOW_SWAP_7", window_swap7 },277{ "WINDOW_SWAP_8", window_swap8 },278{ "WINDOW_SWAP_9", window_swap9 },279{ "WINDOW_SWAP_10", window_swap10 },280{ "YANK_FROM_CUTBUFFER", input_yank_cut_buffer },281{ "NULL", NULL }282};283#define NUMBER_OF_FUNCTIONS (sizeof(key_names) / sizeof(KeyMapNames)) - 1284285/* KeyMap: the structure of the irc keymaps */286typedef struct287{288int key_index;289char * stuff;290char * filename;291char changed;292} KeyMap;293typedef KeyMap * MetaMap[256];294typedef MetaMap ** KeyTable;295static KeyTable keys = NULL;296int curr_keys_size = 0;297static int max_keys_size = 0;298#define MAX_META curr_keys_size - 1299300static void delete_metamap (int i);301302/*303* resize_metamap -- When we need to increase or decrease the number of304* metamaps that the system is handling, you call this function with the305* new size, and everything automagically adjusts from there. This function306* always succeeds if it returns. This function is the callback for the307* /SET META_STATES action.308*/309void resize_metamap (int new_size)310{311int old_size = curr_keys_size;312int i, j;313314/*315* Sorry, just too much will break if you go lower than 5.316*/317if (new_size < 5)318{319say("You can't set META_STATES to less than 5.");320set_int_var(META_STATES_VAR, 5);321}322323if (old_size == new_size)324return; /* What-EVER */325326/*327* If we're growing the meta table, resize and copy the data.328*/329if (old_size < new_size)330{331/*332* Realloc and copy if neccesary333*/334if (new_size > max_keys_size)335{336KeyTable new_keys;337new_keys = new_malloc(sizeof(KeyTable *) * new_size);338339for (i = 0; i < old_size; i++)340new_keys[i] = keys[i];341for (i = old_size; i < new_size; i++)342new_keys[i] = NULL;343new_free((void **)&keys);344keys = new_keys;345max_keys_size = new_size;346}347curr_keys_size = new_size;348}349350/*351* If we're shrinking the meta table, just garbage collect all352* the old bindings, dont actually bother resizing the table.353*/354else355{356for (i = new_size; i < old_size; i++)357delete_metamap(i);358curr_keys_size = new_size;359360/*361* This is a bit tricky -- There might be meta transitions362* in other states that point to the now defunct states.363* If we leave those bindings around, then they will point364* to either meaningless, or bogus data, and either cause365* undefined behavior or a total program crash. So we walk366* all of the remaining states and garbage collect any367* meta transisions that are out of bounds.368*/369for (i = 0; i < new_size; i++)370{371if (!keys[i])372continue;373for (j = 0; j < 256; j++)374if (KEY(i, j) && (KEY(i, j)->key_index <= -new_size))375snew_key(i, j, NULL);376}377}378379set_int_var(META_STATES_VAR, curr_keys_size);380}381382383/*384* new_metamap -- When you "touch" a metamap for the first time,385* the table for the 256 bindings in that metamap must be created, so386* you call this function to do that. You must never call this function387* unless the metamap does not exist, or it will panic.388*/389static void new_metamap (int which)390{391int j;392393if (keys[which])394ircpanic("metamap already exists");395396keys[which] = new_malloc(sizeof(MetaMap));397398for (j = 0; j <= 255; j++)399KEY(which, j) = NULL;400}401402/*403* delete_metamap -- When you're all done with a metamap you can call404* this function to garbage collect it. If there are any bindings in the405* metamap when you call this, they will be summarily disposed of.406*/407static void delete_metamap (int i)408{409int j;410411/* This is cheating, but do i care? ;-) */412for (j = 0; j <= 255; j++)413snew_key(i, j, NULL);414415new_free((char **)&keys[i]);416}417418419420421/* * * * * * * * * * * * * KEY BINDING MANAGEMENT * * * * * * * * * * * * */422/* special interface to new_key for the default key bindings */423static void snew_key (int meta, unsigned chr, char *what)424{425int i;426int j;427428if ((j = lookup_function(what, &i)) == 1)429new_key(meta, chr, i, 0, NULL);430#if 0431else432ircpanic("Something bogus passed to snew_key");433#endif434}435436static void snew_key_from_str (uc *string, char *what)437{438int i;439int meta;440int old_display;441uc chr;442443old_display = window_display;444window_display = 0;445if ((meta = parse_key(string, &chr)) == -1)446return;447window_display = old_display;448449if (lookup_function(what, &i) == 1)450new_key(meta, chr, i, 0, NULL);451452return;453}454455456static void new_key (int meta, unsigned chr, int type, int change, char *stuff)457{458/*459* Create a map first time we bind into it. We have to do this460* Because its possible to do /bind METAX-f when there is not461* otherwise any key bound to METAX.462*/463if (!keys)464return;465if (!keys[meta])466new_metamap(meta);467468if (KEY(meta, chr))469{470if (KEY(meta, chr)->stuff)471new_free(&(KEY(meta, chr)->stuff));472if (KEY(meta, chr)->filename)473new_free(&(KEY(meta, chr)->filename));474new_free(&(KEY(meta, chr)));475KEY(meta, chr) = NULL;476}477478if (type != 0)479{480KEY(meta, chr) = (KeyMap *)new_malloc(sizeof(KeyMap));481KEY(meta, chr)->key_index = type;482KEY(meta, chr)->changed = change;483/* KEY(meta, chr)->filename = m_strdup(current_package());*/484if (stuff)485KEY(meta, chr)->stuff = m_strdup(stuff);486else487KEY(meta, chr)->stuff = NULL;488}489}490491/*492* show_binding: Given an unsigned character 'X' in the meta map 'Y', this493* function will display to the screen the status of that bindings in a494* human-readable way.495*/496static void show_binding (int meta, uc c)497{498char meta_str[8];499500*meta_str = 0;501if (meta < 1 || meta > MAX_META)502meta = 0;503else504sprintf(meta_str, "META%d-", meta);505506if (keys[meta] && KEY(meta, c))507{508#ifdef GUI509if(meta == MAX_META && c < MAX_MOUSE)510say("%s is bound to %s %s", mouse_actions[c],511key_names[(*keys[meta])[c]->key_index].name,512SAFE(KEY(meta, c)->stuff));513else if (KEY(meta, c)->key_index < 0)514#else515if (KEY(meta, c)->key_index < 0)516#endif517say("%s%s is bound to META%d_CHARACTER",518meta_str,519display_key(c),520-(KEY(meta, c)->key_index));521else522say("%s%s is bound to %s %s",523meta_str,524display_key(c),525key_names[KEY(meta, c)->key_index].name,526SAFE(KEY(meta, c)->stuff));527}528else529say("%s%s is bound to NOTHING", meta_str, display_key(c));530}531532#ifdef GUI533void wm_process(int param)534{535void (*func) (char, char *) = NULL;536char *ptr = NULL;537538if (keys[MAX_META] && (*keys[MAX_META]) && (*keys[MAX_META])[param])539{540func = key_names[(*keys[MAX_META])[param]->key_index].func;541ptr = (*keys[MAX_META])[param]->stuff;542}543if (func)544func(param, ptr ? ptr : empty_string);545}546#endif547548/*549* save_bindings: This writes all the key bindings for ircII to the given550* FILE pointer suitable for being /LOADed again in the future.551*/552void save_bindings (FILE *fp, int do_all)553{554int meta, j;555int charsize = charset_size();556char meta_str[10];557558*meta_str = 0;559for (meta = 0; meta <= MAX_META; meta++)560{561if (meta != 0)562sprintf(meta_str, "META%d-", meta);563564for (j = 0; j < charsize; j++)565{566if (keys[meta] && KEY(meta, j) && KEY(meta, j)->changed)567{568if (KEY(meta, j)->key_index < 0)569fprintf(fp, "BIND %s%s META%d\n",570meta_str,571display_key(j),572-(KEY(meta, j)->key_index));573else574fprintf(fp, "BIND %s%s %s %s\n",575meta_str,576display_key(j),577key_names[KEY(meta, j)->key_index].name,578SAFE(KEY(meta, j)->stuff));579}580}581}582}583584/*585* This is a function used by edit_char to retreive the details for a586* specific key binding. This function provides the only external access587* to the key bindings. The arguments are the meta state and the character588* whose information you want to retreive. That information is stored into589* the 'func' and 'name' pointers you pass in.590*591* The function will return 0 if the binding you request is a "normal" one.592* If the binding is "NOTHING", then func will be set to NULL593* If the binding is an action, the func will be set to its callback.594*595* The function will return a positive number if the binding you request is596* a "meta" character.597* The value of 'func' will be NULL but you should not depend on that.598*/599int get_binding (int meta, uc c, KeyBinding *func, char **name)600{601*func = NULL;602*name = NULL;603604if (meta >= 0 && meta <= MAX_META)605{606if (keys[meta] && KEY(meta, c))607{608/*609* If this is a meta binding, return the new meta610* state -- this is a "special" value.611*/612if (KEY(meta, c)->key_index < 0)613return -(KEY(meta, c)->key_index);614615/*616* Otherwise, assign to 'func' and 'name' the617* appropriate values.618*/619*func = key_names[KEY(meta, c)->key_index].func;620*name = KEY(meta, c)->stuff;621}622}623return 0;624}625626627void remove_bindings (void)628{629int i;630631for (i = 0; i <= MAX_META; i++)632delete_metamap(i);633}634635void unload_bindings (const char *filename)636{637int i, j;638639for (i = 0; i <= MAX_META; i++)640{641if (!keys[i])642continue;643644for (j = 0; j < 256; j++)645if (KEY(i, j) && !strcmp(KEY(i, j)->filename, filename))646snew_key(i, j, NULL);647}648}649650651static void show_all_bindings (int meta)652{653int i, j, k;654655if (meta == -1)656{657for (i = 0; i <= MAX_META; i++)658show_all_bindings(i);659return;660}661662if (meta > MAX_META || !keys[meta])663return;664665k = charset_size();666for (j = 0; j < k; j++)667if (KEY(meta, j) && KEY(meta, j)->key_index != 1)668show_binding(meta, j);669}670671/* * * * * * * * * * * * * * PARSEKEY * * * * * * * * * * * * */672/* parsekeycmd: does the PARSEKEY command. */673BUILT_IN_COMMAND(parsekeycmd)674{675int i;676char *arg;677678if ((arg = next_arg(args, &args)) != NULL)679{680int keyval = lookup_function(arg, &i);681682switch (keyval)683{684case 0:685say("No such function %s", arg);686break;687case 1:688if (i < 0)689last_input_screen->meta_hit = -i;690else if (key_names[i].func)691key_names[i].func(0, args);692break;693default:694say("Ambigious function %s", arg);695break;696}697}698}699700/* * * * * * * * * * * * * * RBIND * * * * * * * * * * * * * * */701/*702* rbindcmd: This is the /RBIND command. If you give it a bind action,703* it will show you all of the key bindings that have that action. You704* probably cannot lookup the action NOTHING as it is a magic action.705*/706BUILT_IN_COMMAND(rbindcmd)707{708int f;709char *arg;710int i, j;711int charsize = charset_size();712713if ((arg = next_arg(args, &args)) == NULL)714return; /* No args is a no-op */715716switch (lookup_function(arg, &f))717{718case 0:719say("No such function %s", arg);720return;721722case 1:723break;724725default:726say("Ambigious function %s", arg);727return;728}729730for (i = 0; i <= MAX_META; i++)731{732if (!keys[i])733continue;734735for (j = 0; j < charsize; j++)736if (KEY(i, j) && KEY(i, j)->key_index == f)737show_binding(i, j);738}739}740741742/* * * * * * * * * * * * * * BIND * * * * * * * * * * * * * */743static int grok_meta (const uc *ptr, const uc **end)744{745int meta = -1;746const uc * str;747748/*749* Well, if it is going to be anywhere, META has to be out front,750* so lets slurp it up if its there.751*/752if (!my_strnicmp(ptr, "META", 4))753{754str = ptr = ptr + 4;755while (isdigit(*ptr))756ptr++;757if (*ptr == '_' && !my_strnicmp(ptr, "_CHARACTER", 10))758ptr = ptr + 10;759if (*ptr == '-')760ptr++;761meta = atol(str);762}763764*end = ptr;765return meta;766}767768/*769* copy_redux:770* This converts an ordinary sequence into something more suitable to771* work with, including the redux of ^X into X-64.772* You can then work with the sequence after processing.773*/774void copy_redux (const uc *orig, uc *result)775{776const uc *ptr;777*result = 0;778779for (ptr = orig; ptr && *ptr; ptr++, result++)780{781if (*ptr != '^')782{783*result = *ptr;784continue;785}786787ptr++;788switch (toupper(*ptr))789{790case 0: /* ^<nul> is ^ */791*result = '^';792return;793case '?': /* ^? is DEL */794*result = 0177;795break;796default:797if (toupper(*ptr) < 64)798{799say("Illegal key sequence: ^%c", *ptr);800*result = 0;801return;802}803*result = toupper(*ptr) - 64;804break;805}806}807*result = 0;808return;809}810811/*812* find_meta_map: Finds a meta map that does not already contain a813* binding to the specified character.814*/815int find_meta_map (uc key)816{817int curr = MAX_META;818819for (curr = MAX_META; curr > 4; curr--)820{821if (!keys[curr])822return curr;823824if (!KEY(curr, key))825return curr;826}827828resize_metamap(curr_keys_size + 1);829return MAX_META; /* Well, its empty now */830}831832/*833* Purpose:834* To make sure that the key sequence X is valid upon return.835* Composition of X is: [<key>]*<key>836* Where <key> is an ascii char > 32, or a caret followed by an ascii char.837*838* First, remove the last character839* Second, remove the leading part of X that is a valid meta descriptor840*841* At this point, we have <META> + [<unbound-key>]* + <final-key>842*843* If unbound-key is present, then we have to bind it. The first thing to844* do is find a place where <final-key> can be stashed. Look for the highest845* metamap that has an open spot for <final-key>846*847* Now we have <META> + [<unbound-key>]* + <unbound-key> + <final-key>848* And we now know what meta map the first three parts have to conclude to.849* So that is the return value.850*851* Now we need to build up to that. Repeat this process until there is852* nothing left in the unbound-key segment.853*/854/*855* A lot of magic goes on in this function. The general purpose of this856* function is to take a "key-description" of any form, and canonicalize857* it down into a resulting meta map (which is the return value), and return858* the final character in 'term'. Older algorithms only allowed you to859* specify META(X)-(Y), where X is the meta value to be returned and Y is860* the character, and anything else was an error. Now we allow you to specify861* any arbitrary string -- if the leading part of the string is already bound862* to a META key, then we can deal with that. If the leading part of the863* string is NOT bound to anything in particular, then we will bind it FOR864* you, and the resulting meta state is returned. This allows things like865* this to work:866*867* /BIND META2-C BIND-ACTION (Specify a meta map directly)868* /BIND ^[[A BIND-ACTION (^[[ is bound to META2 by default)869* /BIND ^[[11~ BIND-ACTION (Force us to make suer ^[[11 is bound870* to a meta map before returning.)871*/872static int parse_key (const uc *sequence, uc *term)873{874uc *copy;875uc *end;876int return_meta = 0;877int meta;878uc last_character;879uc terminal_character;880int last;881int somethingN;882#ifdef GUI883int mouse;884#endif885886/*887* Make a local copy of the string to be bound. Redux all of888* the ^x modifers to their literal control characters.889*/890copy = alloca(strlen(sequence) + 4);891copy_redux(sequence, copy);892end = copy + strlen(copy) - 1;893894#ifdef GUI895for( mouse = 0; mouse < MAX_MOUSE; mouse++)896{897if (!my_strnicmp(sequence, mouse_actions[mouse], strlen(mouse_actions[mouse])))898{899*term=(char)mouse;900return MAX_META;901}902}903#endif904905if (x_debug & DEBUG_AUTOKEY)906yell("Starting with COPY := [%s]", copy);907/*908* Remove any leading META description909*/910if ((meta = grok_meta(copy, (const uc **)©)) == -1)911meta = 0;912913if (x_debug & DEBUG_AUTOKEY)914yell("After META grokked, COPY := [%s]", copy);915916/*917* Remove any leading characters that also comprise a META918* description919*/920while (copy[0] && copy[1])921{922if (keys[meta] && KEY(meta, *copy) &&923KEY(meta, *copy)->key_index < 0)924{925meta = -(KEY(meta, *copy)->key_index);926copy++;927if (x_debug & DEBUG_AUTOKEY)928{929yell("First character of COPY switches to meta [%d]", meta);930yell("After META grokked, COPY := [%s]", copy);931}932continue;933}934break;935}936937if (x_debug & DEBUG_AUTOKEY)938yell("After ALL META grokked, COPY := [%s]", copy);939940/*941* Check to see if the entire sequence was just a meta modifier942* or if it is a META-KEY modifier. Either way, we're done.943*/944if (!copy[0] || !copy[1])945{946*term = copy[0];947return meta;948}949950/*951* Right now the input boils down to this:952*953* input := SOME_CHARACTERS + TERMINAL_CHARACTER + LAST_CHARACTER954* SOME_CHARACTERS := <key>*955* TERMINAL_CHARACTER := <key>956* LAST_CHARACTER := <key>957*958* The previous check assures that 'terminal character' is not959* an empty value at this point.960*/961last_character = *end;962*end-- = 0;963terminal_character = *end;964*end-- = 0;965966if (x_debug & DEBUG_AUTOKEY)967{968yell("Starting to work on the string:");969yell("SOME_CHARACTERS := [%s] (%d)", copy, strlen(copy));970yell("TERMINAL_CHARACTER := [%c]", terminal_character);971yell("LAST_CHARACTER := [%c]", last_character);972}973974975/*976* Our ultimate goal is to return when the operation:977* /bind META<something>-LAST_CHARACTER <binding>978* will succeed. So we need to find a place to put LAST_CHARACTER.979*/980last = return_meta = find_meta_map(last_character);981if (x_debug & DEBUG_AUTOKEY)982{983yell("FIND_META_MAP says we can put [%c] in META [%d]",984last_character, return_meta);985}986987/*988* So now we need to work backwards through the string linking989* each of the characters to the next one. Starting with990* TERMINAL_CHARACTER, we find a meta map where that can be linked991* from (that map is somethingN1). We then do:992*993* /bind META<somethingN1>-TERMINAL_CHARACTER META<LAST>994*995* Where 'last' is the most previous meta map we linked to, starting996* with 'something'.997*/998while (*copy)999{1000if (x_debug & DEBUG_AUTOKEY)1001{1002yell("COPY: [%s] (%d)", copy, strlen(copy));1003yell("Now we are going to bind the [%c] character to meta [%d] somehow.",1004terminal_character, last);1005}10061007/*1008* <something> is any meta map such that:1009* /bind META<somethingN>-[TERMINAL CHARACTER] META<something>1010*/1011somethingN = find_meta_map(terminal_character);1012if (x_debug & DEBUG_AUTOKEY)1013yell("FIND_META_MAP says we can do this in META [%d]", somethingN);10141015new_key(somethingN, terminal_character, -last, 1, NULL);1016show_binding(somethingN, terminal_character);10171018/*1019* Now we walk backwards in the string: 'last' now becomes1020* the meta map we just linked, and we pop TERMINAL_CHARACTER1021* off the end of SOME_CHARACTERS. We repeat this until1022* SOME_CHARACTERS is empty.1023*/1024last = somethingN;1025terminal_character = *end;1026*end-- = 0;1027}10281029/*1030* Make the final link from the initial meta state to our newly1031* constructed chain...1032*/1033new_key(meta, terminal_character, -last, 1, NULL);1034show_binding(meta, terminal_character);10351036/*1037* Return the interesting information1038*/1039*term = last_character;1040return return_meta;10411042#if 01043/* The rest of this isnt finished, hense is unsupported */1044say("The bind cannot occur because the character sequence to bind contains a leading substring that is bound to something else.");1045return -1;1046#endif1047}10481049/*1050* bindcmd: The /BIND command. The general syntax is:1051*1052* /BIND ([key-descr] ([bind-command] ([args])))1053* Where:1054* KEY-DESCR := ([^]C | META[num])1055* BIND-COMMAND := <Any string in the key_names lookup table>1056*1057* If given no arguments, this command shows all non-empty bindings1058* current registered.1059*1060* If given one argument, that argument is to be a description of a valid1061* key sequence. The command will show the binding of that sequence.1062*1063* If given two arguments, the first argument is to be a description of a1064* valid key sequence and the second argument is to be a valid binding1065* command followed by any optionally appropriate arguments. The key1066* sequence is then bound to that action.1067*1068* The special binding command "NOTHING" actually unbinds the key.1069*/1070BUILT_IN_COMMAND(bindcmd)1071{1072uc *key,1073*function;1074uc *newkey;1075int meta;1076uc dakey;1077int bi_index;1078int cnt,1079i;10801081/*1082* See if they specified a key argument. If they didnt, show all1083* binds and return1084*/1085if ((key = new_next_arg(args, &args)) == NULL)1086{1087show_all_bindings(-1);1088return;1089}10901091/*1092* Grok any flags (only one, for now)1093*/1094if (*key == '-')1095{1096if (!my_strnicmp(key + 1, "DEFAULTS", 1))1097{1098init_keys();1099init_keys2();1100}1101return;1102}110311041105/*1106* Grok the key argument and see what we can make of it1107* If there is an error at this point, dont continue.1108* Most of the work is done here.1109*/11101111newkey = get_term_capability(key, 0, 1);1112if ((meta = parse_key(newkey ? newkey : key, &dakey)) == -1)1113if (!newkey || (parse_key(key, &dakey) == -1))1114return;11151116/*1117* See if they specified an action argument. If they didnt, then1118* check to see if they specified /bind METAX or if they specified1119* /bind <char sequence>, and output as is appropriate.1120*/1121if ((function = next_arg(args, &args)) == NULL)1122{1123/* They did /bind ^C */1124if (dakey)1125show_binding(meta, dakey);11261127/* They did /bind meta2 */1128else1129show_all_bindings(meta);11301131return;1132}11331134/*1135* Look up the action they want to take. If it is invalid, tell1136* them so, if it is ambiguous, show the possible choices, and if1137* if it valid, then actually do the bind action. Note that if we1138* do the bind, we do a show() so the user knows we took the action.1139*/1140switch ((cnt = lookup_function(function, &bi_index)))1141{1142case 0:1143say("No such function: %s", function);1144break;1145case 1:1146if (meta < 1 || meta > MAX_META)1147meta = 0;1148new_key(meta, dakey, bi_index, 1, *args ? args : NULL);1149show_binding(meta, dakey);1150break;1151default:1152say("Ambiguous function name: %s", function);1153for (i = 0; i < cnt; i++, bi_index++)1154put_it("%s", key_names[bi_index].name);1155break;1156}1157}11581159/* * * * * * * * * * * BINDING ACTIONS * * * * * * * * * */1160/* I hate typedefs... */11611162/*1163* lookup_function: When you want to convert a "binding" name (such as1164* BACKSPACE or SELF_INSERT) over to its offset in the binding lookup table,1165* you must call this function to retreive that offset. The first argument1166* is the name you want to look up, and the second argument is where the1167* offset is to be stored.1168*1169* Return value: (its tricky)1170* -1 -- The name is a META binding that is invalid.1171* Zero -- The name is not a valid binding name.1172* One -- The name is a valid, unambiguous binding name.1173* If it is a META binding, lf_index will be negative,1174* Otherwise, lf_index will be positive.1175* Other -- The name is an ambiguous (therefore invalid) binding name.1176*1177* In the case of a return value of any positive value, "lf_index" will be1178* set to the first item that matches the 'name'. For all other return1179* values, "lf_index" will have the value -1.1180*/1181static int lookup_function (const uc *orig_name, int *lf_index)1182{1183int len,1184cnt,1185i;1186uc *name, *breakage;11871188if (!orig_name)1189{1190*lf_index = 0;1191return 1;1192}11931194breakage = name = LOCAL_COPY(orig_name);1195upper(name);1196len = strlen(name);11971198*lf_index = -1;11991200/* Handle "META" descriptions especially. */1201if (!strncmp(name, "META", 4))1202{1203const uc * endp;1204int meta;12051206if ((meta = grok_meta(name, &endp)) < 0)1207return meta;1208else1209{1210*lf_index = -meta;1211return 1;1212}1213}12141215for (cnt = 0, i = 0; i < NUMBER_OF_FUNCTIONS; i++)1216{1217if (strncmp(name, key_names[i].name, len) == 0)1218{1219cnt++;1220if (*lf_index == -1)1221*lf_index = i;1222}1223}1224if (*lf_index == -1)1225return 0;1226if (strcmp(name, key_names[*lf_index].name) == 0)1227return 1;1228else1229return cnt;1230}123112321233/* I dont know where this belongs. */1234/*1235* display_key: Given a (possibly unprintable) unsigned character 'c',1236* convert that character into a printable string. For characters less1237* than 32, and the character 127, they will be converted into the "control"1238* sequence by having a prepended caret ('^'). Other characters will be1239* left alone. The return value belongs to the function -- dont mangle it.1240*/1241static uc * display_key (uc c)1242{1243static uc key[3];12441245key[2] = (char) 0;1246if (c < 32)1247{1248key[0] = '^';1249key[1] = c + 64;1250}1251else if (c == '\177')1252{1253key[0] = '^';1254key[1] = '?';1255}1256else1257{1258key[0] = c;1259key[1] = (char) 0;1260}1261return (key);1262}12631264char *convert_to_keystr(char *key)1265{1266int ret, loc;1267static char keyloc[80];1268ret = lookup_function(key, &loc);1269*keyloc = 0;1270if (ret == 1)1271{1272char meta_str[8];1273int i, j;1274int charsize = charset_size();1275*meta_str = 0;12761277for (i = 0; i <= MAX_META; i++)1278{1279if (!keys[i])1280continue;1281for (j = 0; j < charsize; j++)1282if (KEY(i, j) && KEY(i, j)->key_index == loc)1283{1284if (i > 0)1285sprintf(meta_str, "META%d-", i);1286sprintf(keyloc, "%s%s", meta_str, display_key(j));1287return keyloc;1288}1289}1290}1291return keyloc;1292}12931294/* * * * * * * * * * * * * * * * * * INITIALIZATION * * * * * * * * * * * */1295/*1296* This is where you put all the default key bindings. This is a lot1297* simpler, just defining those you need, instead of all of them, isnt1298* it? And it takes up so much less memory, too...1299*/1300void init_keys (void)1301{1302int i;13031304/*1305* Make sure the meta map is big enough to hold all these bindings.1306*/1307remove_bindings();1308resize_metamap(40); /* Whatever. */13091310/*1311* All the "default" bindings are self_insert unless we bind1312* them differently1313*/1314for (i = 1; i <= 255; i++)1315snew_key(0, i, "SELF_INSERT");13161317/* "default" characters that arent self_insert */1318snew_key(0, 1, "BEGINNING_OF_LINE"); /* ^A */1319snew_key(0, 2, "BOLD"); /* ^B */1320snew_key(0, 4, "DELETE_CHARACTER"); /* ^D */1321snew_key(0, 5, "CHANGE_TO_SPLIT"); /* ^E */1322snew_key(0, 6, "WHOLEFT"); /* ^F */1323snew_key(0, 8, "BACKSPACE"); /* ^H (delete) */13241325snew_key(0, 9, "TAB_COMPLETION"); /* ^I (tab) */1326snew_key(0, 10, "SEND_LINE"); /* ^J (enter) */1327snew_key(0, 11, "JOIN_LAST_INVITE"); /* ^K */1328snew_key(0, 12, "REFRESH_SCREEN"); /* ^L (linefeed) */1329snew_key(0, 13, "SEND_LINE"); /* ^M (return) */1330snew_key(0, 14, "QUOTE_CHARACTER"); /* ^N */1331snew_key(0, 15, "IGNORE_NICK"); /* ^O */1332snew_key(0, 16, "BACKWARD_HISTORY"); /* ^P */13331334snew_key(0, 17, "QUOTE_CHARACTER"); /* ^Q */1335snew_key(0, 18, "NICK_COMPLETION"); /* ^R */1336snew_key(0, 19, "TOGGLE_STOP_SCREEN"); /* ^S */1337snew_key(0, 20, "TRANSPOSE_CHARACTERS"); /* ^T */1338snew_key(0, 21, "ERASE_LINE"); /* ^U */1339snew_key(0, 22, "REVERSE"); /* ^V */1340snew_key(0, 23, "META2_CHARACTER"); /* ^W */1341snew_key(0, 24, "SWITCH_CHANNELS"); /* ^X */13421343snew_key(0, 25, "YANK_FROM_CUTBUFFER"); /* ^Y */1344#ifdef ALLOW_STOP_IRC1345#ifndef PUBLIC_ACCESS1346snew_key(0, 26, "STOP_IRC"); /* ^Z */1347#endif1348#endif1349snew_key(0, 27, "META1_CHARACTER"); /* ^[ (escape) */1350snew_key(0, 29, "AUTOREPLY"); /* ^] */1351snew_key(0, 31, "UNDERLINE"); /* ^_ */13521353snew_key(0, 127, "BACKSPACE"); /* ^? (delete) */13541355/*1356* european keyboards (and probably others) use the eigth bit1357* for extended characters. Having these keys bound by default1358* causes them lots of grief, so unless you really want to use1359* these, they are commented out.1360*/1361#ifdef EMACS_KEYBINDS1362snew_key(0, 188, "SCROLL_START");1363snew_key(0, 190, "SCROLL_END");1364snew_key(0, 226, "BACKWARD_WORD");1365snew_key(0, 228, "DELETE_NEXT_WORD");1366snew_key(0, 229, "SCROLL_END");1367snew_key(0, 230, "FORWARD_WORD");1368snew_key(0, 232, "DELETE_PREVIOUS_WORD");1369snew_key(0, 255, "DELETE_PREVIOUS_WORD");1370#endif13711372/* meta 1 characters */1373snew_key(1, 27, "COMMAND_COMPLETION");1374snew_key(1, 46, "CLEAR_SCREEN");1375snew_key(1, 60, "SCROLL_START");1376snew_key(1, 62, "SCROLL_END");1377snew_key(1, 79, "META2_CHARACTER");1378snew_key(1, 91, "META2_CHARACTER");1379snew_key(1, 98, "BACKWARD_WORD");1380snew_key(1, 100, "DELETE_NEXT_WORD");1381snew_key(1, 101, "SCROLL_END");1382snew_key(1, 102, "FORWARD_WORD");1383snew_key(1, 104, "DELETE_PREVIOUS_WORD");1384snew_key(1, 110, "SCROLL_FORWARD");1385snew_key(1, 112, "SCROLL_BACKWARD");1386snew_key(1, 127, "DELETE_PREVIOUS_WORD");13871388snew_key(1, '1', "WINDOW_SWAP_1");1389snew_key(1, '2', "WINDOW_SWAP_2");1390snew_key(1, '3', "WINDOW_SWAP_3");1391snew_key(1, '4', "WINDOW_SWAP_4");1392snew_key(1, '5', "WINDOW_SWAP_5");1393snew_key(1, '6', "WINDOW_SWAP_6");1394snew_key(1, '7', "WINDOW_SWAP_7");1395snew_key(1, '8', "WINDOW_SWAP_8");1396snew_key(1, '9', "WINDOW_SWAP_9");1397snew_key(1, '0', "WINDOW_SWAP_10");13981399/* meta 2 characters */1400#ifdef ALLOW_STOP_IRC1401#ifndef PUBLIC_ACCESS1402snew_key(2, 26, "STOP_IRC");1403#endif1404#endif14051406snew_key(2, 110, "SWAP_NEXT_WINDOW");1407snew_key(2, 112, "PREVIOUS_WINDOW");14081409#ifdef __EMX__1410/* meta 3 characters */1411snew_key(2, '[', "META3_CHARACTER");1412snew_key(2, '1', "META3_CHARACTER");14131414snew_key(1, 71, "NEW_BEGINNING_OF_LINE");1415snew_key(1, 83, "TOGGLE_CLOAK");1416snew_key(1, 79, "NEW_SCROLL_END");1417snew_key(1, 73, "NEW_SCROLL_BACKWARD");1418snew_key(1, 81, "NEW_SCROLL_FORWARD");14191420snew_key(2, '?', "WINDOW_HELP");1421snew_key(2, '+', "WINDOW_GROW_ONE");1422snew_key(2, '-', "WINDOW_SHRINK_ONE");1423snew_key(2, 'm', "WINDOW_MOVE");1424snew_key(2, 'l', "WINDOW_LIST");1425snew_key(2, 'k', "WINDOW_KILL");1426snew_key(2, 'b', "WINDOW_BALANCE");1427snew_key(2, 'h', "WINDOW_HIDE");14281429snew_key(2, '[', "META3_CHARACTER");1430snew_key(2, '1', "META3_CHARACTER");14311432#else /* __EMX__ */1433snew_key(2, '?', "WINDOW_HELP");1434snew_key(2, '+', "WINDOW_GROW_ONE");1435snew_key(2, '-', "WINDOW_SHRINK_ONE");1436snew_key(2, 'm', "WINDOW_MOVE");1437snew_key(2, 'l', "WINDOW_LIST");1438snew_key(2, 'k', "WINDOW_KILL");1439snew_key(2, 'b', "WINDOW_BALANCE");1440snew_key(2, 'h', "WINDOW_HIDE");1441snew_key(2, '[', "META3_CHARACTER");1442snew_key(2, 70, "SCROLL_START"); /* Freebsd home */1443snew_key(2, 71, "SCROLL_FORWARD"); /* Freebsd pgdown */1444snew_key(2, 72, "SCROLL_END"); /* Freebsd end */1445snew_key(2, 73, "SCROLL_BACKWARD"); /* Freebsd pgup */1446snew_key(2, '1', "META32_CHARACTER");1447snew_key(2, '4', "META33_CHARACTER");1448snew_key(2, '5', "META30_CHARACTER");1449snew_key(2, '6', "META31_CHARACTER");1450#endif14511452/* meta 4 characters -- vi key mappings */1453snew_key(4, 8, "BACKWARD_CHARACTER");1454snew_key(4, 32, "FORWARD_CHARACTER");1455snew_key(4, 65, "META4");1456snew_key(4, 72, "BACKWARD_CHARACTER");1457snew_key(4, 73, "META4");1458snew_key(4, 74, "FORWARD_HISTORY");1459snew_key(4, 75, "BACKWARD_HISTORY");1460snew_key(4, 76, "FORWARD_CHARACTER");1461snew_key(4, 88, "DELETE_CHARACTER");1462snew_key(4, 97, "META4");1463snew_key(4, 104, "BACKWARD_CHARACTER");1464snew_key(4, 105, "META4");1465snew_key(4, 106, "FORWARD_HISTORY");1466snew_key(4, 107, "BACKWARD_HISTORY");1467snew_key(4, 108, "FORWARD_CHARACTER");1468snew_key(4, 120, "DELETE_CHARACTER");14691470/* I used 30-something to keep them out of others' way */1471snew_key(30, '~', "SCROLL_BACKWARD");1472snew_key(31, '~', "SCROLL_FORWARD");1473snew_key(32, '~', "SCROLL_START");1474snew_key(33, '~', "SCROLL_END");1475}14761477#define LKEY(x, y) \1478{ \1479char *l = get_term_capability(#x, 0, 1); \1480if (l) \1481snew_key_from_str(l, #y); \1482}14831484void init_keys2 (void)1485{1486/* keys bound from terminfo/termcap */1487LKEY(key_up, BACKWARD_HISTORY)1488LKEY(key_down, FORWARD_HISTORY)1489LKEY(key_left, BACKWARD_CHARACTER)1490LKEY(key_right, FORWARD_CHARACTER)1491LKEY(key_ppage, SCROLL_BACKWARD)1492LKEY(key_npage, SCROLL_FORWARD)1493LKEY(key_home, SCROLL_START)1494LKEY(key_end, SCROLL_END)1495LKEY(key_ic, TOGGLE_INSERT_MODE)1496/*LKEY(key_dc, DELETE_CHARACTER)*/1497LKEY(key_f1, CHELP)1498LKEY(key_f2, CHANNEL_CHOPS)1499LKEY(key_f3, CHANNEL_NONOPS)1500#ifdef WANT_CDCC1501LKEY(key_f4, CDCC_PLIST)1502#endif1503LKEY(key_f5, DCC_PLIST)1504LKEY(key_f6, DCC_STATS)1505LKEY(key_dc, TOGGLE_CLOAK)1506}150715081509void disable_stop(void)1510{1511snew_key(0, 26, "SELF_INSERT");1512}151315141515