diff options
Diffstat (limited to 'input.c')
-rw-r--r-- | input.c | 1210 |
1 files changed, 1210 insertions, 0 deletions
diff --git a/input.c b/input.c new file mode 100644 index 00000000..9abbb5d7 --- /dev/null +++ b/input.c @@ -0,0 +1,1210 @@ +/** \file input.c + +Functions for reading a character of input from stdin, using the +inputrc information for key bindings. + +The inputrc file format was invented for the readline library. The +implementation in fish is as of yet incomplete. + +*/ + +#include "config.h" + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <termios.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <unistd.h> +#include <wchar.h> + +#if HAVE_NCURSES_H +#include <ncurses.h> +#else +#include <curses.h> +#endif + + +#if HAVE_TERMIO_H +#include <termio.h> +#endif + +#include <term.h> +#include <signal.h> +#include <dirent.h> +#include <wctype.h> + + +#include "util.h" +#include "wutil.h" +#include "reader.h" +#include "proc.h" +#include "common.h" +#include "sanity.h" +#include "input_common.h" +#include "input.h" +#include "parser.h" +#include "env.h" +#include "expand.h" + +static void input_read_inputrc( wchar_t *fn ); + +/** + Array containing characters which have been peeked by the escape + sequence matching functions and returned +*/ + +typedef struct +{ + wchar_t *seq; /**< Character sequence which generates this event */ + wchar_t *seq_desc; /**< Description of the character sequence suitable for printing on-screen */ + wchar_t *command; /**< command that should be evaluated by this mapping */ + +} + mapping; + +/** + Names of all the readline functions supported +*/ +const wchar_t *name_arr[] = +{ + L"beginning-of-line", + L"end-of-line", + L"forward-char", + L"backward-char", + L"forward-word", + L"backward-word", + L"history-search-backward", + L"history-search-forward", + L"delete-char", + L"backward-delete-char", + L"kill-line", + L"yank", + L"yank-pop", + L"complete", + L"beginning-of-history", + L"end-of-history", + L"delete-line", + L"backward-kill-line", + L"kill-whole-line", + L"kill-word", + L"backward-kill-word", + L"dump-functions", + L"clear-screen", + L"exit", + L"history-token-search-backward", + L"history-token-search-forward", + L"self-insert", + L"null" +} + ; + +/** + Description of each supported readline function +*/ +const wchar_t *desc_arr[] = +{ + L"Move to beginning of line", + L"Move to end of line", + L"Move forward one character", + L"Move backward one character", + L"Move forward one word", + L"Move backward one word", + L"Search backward through list of previous commands", + L"Search forward through list of previous commands", + L"Delete one character forward", + L"Delete one character backward", + L"Move contents from cursor to end of line to killring", + L"Paste contents of killring", + L"Rotate to previous killring entry", + L"Guess the rest of the next input token", + L"Move to first item of history", + L"Move to last item of history", + L"Clear current line", + L"Move contents from beginning of line to cursor to killring", + L"Move entire line to killring", + L"Move next word to killring", + L"Move previous word to killring", + L"Write out keybindings", + L"Clear entire screen", + L"Quit the running program", + L"Search backward through list of previous commands for matching token", + L"Search forward through list of previous commands for matching token", + L"Insert the pressed key", + L"Do nothing" +} + ; + +/** + Internal code for each supported readline function +*/ +const wchar_t code_arr[] = +{ + R_BEGINNING_OF_LINE, + R_END_OF_LINE, + R_FORWARD_CHAR, + R_BACKWARD_CHAR, + R_FORWARD_WORD, + R_BACKWARD_WORD, + R_HISTORY_SEARCH_BACKWARD, + R_HISTORY_SEARCH_FORWARD, + R_DELETE_CHAR, + R_BACKWARD_DELETE_CHAR, + R_KILL_LINE, + R_YANK, + R_YANK_POP, + R_COMPLETE, + R_BEGINNING_OF_HISTORY, + R_END_OF_HISTORY, + R_DELETE_LINE, + R_BACKWARD_KILL_LINE, + R_KILL_WHOLE_LINE, + R_KILL_WORD, + R_BACKWARD_KILL_WORD, + R_DUMP_FUNCTIONS, + R_CLEAR_SCREEN, + R_EXIT, + R_HISTORY_TOKEN_SEARCH_BACKWARD, + R_HISTORY_TOKEN_SEARCH_FORWARD, + R_SELF_INSERT, + R_NULL +} + ; + + +/** + List of all key bindings, as mappings from one sequence to either a character or a command +*/ +static hash_table_t all_mappings; + +static array_list_t *current_mode_mappings, *current_application_mappings, *global_mappings; + +/** + Number of nested conditional statement levels that are not evaluated +*/ +static int inputrc_skip_block_count=0; +/** + Number of nested conditional statements that have evaluated to true +*/ +static int inputrc_block_count=0; + +/** + True if syntax errors where found in the inputrc file +*/ +static int inputrc_error = 0; + +wchar_t input_get_code( wchar_t *name ) +{ + + int i; + for( i = 0; i<(sizeof( code_arr )/sizeof(wchar_t)) ; i++ ) + { + if( wcscmp( name, name_arr[i] ) == 0 ) + { + return code_arr[i]; + } + } + return -1; +} + +/** + Returns the function name for the given function code. +*/ +static const wchar_t *input_get_name( wchar_t c ) +{ + + int i; + for( i = 0; i<(sizeof( code_arr )/sizeof(wchar_t)) ; i++ ) + { + if( c == code_arr[i] ) + { + return name_arr[i]; + } + } + return 0; +} + +/** + Returns the function description for the given function code. +*/ +static const wchar_t *input_get_desc( wchar_t c ) +{ + + int i; + for( i = 0; i<(sizeof( code_arr )/sizeof(wchar_t)) ; i++ ) + { + if( c == code_arr[i] ) + { + return desc_arr[i]; + } + } + return 0; +} + +void input_set_mode( wchar_t *name ) +{ + current_mode_mappings = (array_list_t *)hash_get( &all_mappings, name ); +} + +void input_set_application( wchar_t *name ) +{ + current_application_mappings = (array_list_t *)hash_get( &all_mappings, name ); +} + +static array_list_t *get_mapping( const wchar_t *mode ) +{ + + array_list_t * mappings = (array_list_t *)hash_get( &all_mappings, mode ); + + if( !mappings ) + { + mappings = malloc( sizeof( array_list_t )); + al_init( mappings ); + + hash_put( &all_mappings, wcsdup(mode), mappings ); + + } + return mappings; + +} + + +void add_mapping( const wchar_t *mode, + const wchar_t *s, + const wchar_t *d, + const wchar_t *c ) +{ + int i; + + array_list_t *mappings; + + if( s == 0 ) + return; + + if( mode == 0 ) + return; + + mappings = get_mapping( mode ); + + for( i=0; i<al_get_count( mappings); i++ ) + { + mapping *m = (mapping *)al_get( mappings, i ); + if( wcscmp( m->seq, s ) == 0 ) + { + free( m->command ); + free( m->seq_desc ); + m->seq_desc = wcsdup(d ); + m->command=wcsdup(c); + return; + } + } + + mapping *m = malloc( sizeof( mapping ) ); + m->seq = wcsdup( s ); + m->seq_desc = wcsdup(d ); + m->command=wcsdup(c); + al_push( mappings, m ); +} + +/** + Compare sort order for two keyboard mappings. This function is made + to be suitable for use with the qsort method. +*/ +static int mapping_compare( const void *a, const void *b ) +{ + mapping *c = *(mapping **)a; + mapping *d = *(mapping **)b; + +// fwprintf( stderr, L"%ls %ls\n", c->seq_desc, d->seq_desc ); + + return wcscmp( c->seq_desc, d->seq_desc ); + +} + + + +/** + Print a listing of all keybindings and a description of each + function. This is used by the dump-functions readline function. +*/ +static void dump_functions() +{ +/* int i; + fwprintf( stdout, L"\n" ); + + qsort(current_mappings.arr, + al_get_count( &mappings), + sizeof( void*), + &mapping_compare ); + + for( i=0; i<al_get_count( &mappings ); i++ ) + { + mapping *m = (mapping *)al_get( &mappings, i ); + +// write_sequence( m->seq ); +fwprintf( stdout, +L"%ls: %ls\n", +m->seq_desc, +m->command ); +} +repaint();*/ +} + + +/** + Unescape special character from the specified inputrc-style key sequence. + + \\C-a is expanded to 1, etc. +*/ +static wchar_t *input_expand_sequence( const wchar_t *in ) +{ + wchar_t *res = malloc( sizeof( wchar_t)*(4*wcslen(in)+1)); + wchar_t *out=res; + int error = 0; + + while( *in && !error) + { + switch( *in ) + { + case L'\\': + { + in++; +// fwprintf( stderr, L"Backslash\n" ); + switch( *in ) + { + case L'\0': + error = 1; + break; + + case L'e': + *(out++)=L'\e'; + break; + + case L'\\': + case L'\"': + case L'\'': + *(out++)=*in; + break; + + case L'b': + *(out++)=L'\b'; + break; + + case L'd': + { + wchar_t *str = str2wcs( key_dc ); + wchar_t *p=str; + if( p ) + { + while( *p ) + { + *(out++)=*(p++); + } + free( str ); + } + break; + } + + case L'f': + *(out++)=L'\f'; + break; + + case L'n': + *(out++)=L'\n'; + break; + + case L'r': + *(out++)=L'\r'; + break; + + case L't': + *(out++)=L'\t'; + break; + + case L'v': + *(out++)=L'\v'; + break; + + case L'C': + { + //fwprintf( stderr, L"Control\n" ); + in++; + if( *in != L'-' ) + { + error=1; +// fwprintf( stderr, L"no dash\n" ); + break; + } + in++; + + if( (*in >= L'a') && + (*in <= L'z') ) + { + *(out++)=*in-L'a'+1; + break; + } + + if( (*in >= L'A') && + (*in <= L'Z') ) + { + *(out++)=*in-L'A'+1; + break; + } +// fwprintf( stderr, L"No char after\n" ); + error = 1; + + break; + } + + case L'M': + { + in++; + if( *in != L'-' ) + { + error=1; +// fwprintf( stderr, L"no dash\n" ); + break; + } + in++; + + if( (*in >= L'a') && + (*in <= L'z') ) + { + *(out++)=L'\e'; + *(out++)=*in; + break; + } + + if( (*in >= L'A') && + (*in <= L'Z') ) + { + *(out++)=L'\e'; + *(out++)=*in; + break; + } +// fwprintf( stderr, L"No char after\n" ); + error = 1; + + break; + } + + default: + { + *(out++)=*in; + break; + } + + } + + break; + } + default: + { + *(out++)=*in; + break; + } + } + in++; + } + + if( error ) + { +// fwprintf( stderr, L"%ls had errors\n", in_orig ); + free( res); + res=0; + } + else + { +// fwprintf( stderr, L"%ls translated ok\n", in_orig ); + *out = L'\0'; + } + + return res; +} + + +void input_parse_inputrc_line( wchar_t *cmd ) +{ + wchar_t *p=cmd; + + /* Make all whitespace into space characters */ + while( *p ) + { + if( *p == L'\t' || *p == L'\r' ) + *p=L' '; + p++; + } + + /* Remove spaces at beginning/end */ + while( *cmd == L' ' ) + cmd++; + + p = cmd + wcslen(cmd)-1; + while( (p >= cmd) && (*p == L' ') ) + { + *p=L'\0'; + p--; + } + + /* Skip comments */ + if( *cmd == L'#' ) + return; + + /* Skip empty lines */ + if( *cmd == L'\0' ) + return; + + if( wcscmp( L"$endif", cmd) == 0 ) + { + if( inputrc_skip_block_count ) + { + inputrc_skip_block_count--; +/* + if( !inputrc_skip_block_count ) + fwprintf( stderr, L"Stop skipping\n" ); + else + fwprintf( stderr, L"Decrease skipping\n" ); +*/ + } + else + { + if( inputrc_block_count ) + { + inputrc_block_count--; +// fwprintf( stderr, L"End of active block\n" ); + } + else + { + inputrc_error = 1; + debug( 1, + L"Mismatched $endif in inputrc file" ); + } + } + return; + } + + if( wcscmp( L"$else", cmd) == 0 ) + { + if( inputrc_skip_block_count ) + { + if( inputrc_skip_block_count == 1 ) + { + inputrc_skip_block_count--; + inputrc_block_count++; + } + + } + else + { + inputrc_skip_block_count++; + inputrc_block_count--; + } + + return; + } + + if( inputrc_skip_block_count ) + { + if( wcsncmp( L"$if ", cmd, wcslen( L"$if " )) == 0 ) + inputrc_skip_block_count++; +// fwprintf( stderr, L"Skip %ls\n", cmd ); + + return; + } + + if( *cmd == L'\"' ) + { + + wchar_t *key; + wchar_t *val; + wchar_t *sequence; + wchar_t prev=0; + + + cmd++; + key=cmd; + + for( prev=0; ;prev=*cmd,cmd++ ) + { + if( !*cmd ) + { + debug( 1, + L"Mismatched quote" ); + inputrc_error = 1; + return; + } + + if(( *cmd == L'\"' ) && prev != L'\\' ) + break; + + } + *cmd=0; + cmd++; + if( *cmd != L':' ) + { + debug( 1, + L"Expected a \':\'" ); + inputrc_error = 1; + return; + } + cmd++; + while( *cmd == L' ' ) + cmd++; + + val = cmd; + + sequence = input_expand_sequence( key ); + add_mapping( L"global", sequence, key, val ); + +// fwprintf( stderr, L"Map %ls to %ls\n", key, val ); + + free( sequence ); + + //fwprintf( stderr, L"Remainder \'%ls\', endchar %d\n", cmd, *cmd ); + + //fwprintf( stderr, L"%ls -> %ls\n", key, val ); + + return; + } + else if( wcsncmp( L"$include ", cmd, wcslen(L"$include ") ) == 0 ) + { + wchar_t *tmp; + + cmd += wcslen( L"$include "); + while( *cmd == L' ' ) + cmd++; + tmp=wcsdup(cmd); + tmp = expand_tilde(tmp); + if( tmp ) + input_read_inputrc( tmp ); + free(tmp); + return; + } + else if( wcsncmp( L"set", cmd, wcslen( L"set" ) ) == 0 ) + { + wchar_t *set, *key, *value, *end; + wchar_t *state; + + set = wcstok( cmd, L" \t", &state ); + key = wcstok( 0, L" \t", &state ); + value = wcstok( 0, L" \t", &state ); + end = wcstok( 0, L" \t", &state ); + + if( wcscmp( set, L"set" ) != 0 ) + { + debug( 1, L"I don\'t know what %ls means", set ); + } + else if( end ) + { + debug( 1, L"Expected end of line, got '%ls'", end ); + + } + else if( (!key) || (!value) ) + { + debug( 1, L"Syntax: set KEY VALUE" ); + } + else + { + if( wcscmp( key, L"editing-mode" ) == 0 ) + { +// current_mode_mappings = get_mapping( value ); + } + } + + return; + } + else if( wcsncmp( L"$if ", cmd, wcslen( L"$if " )) == 0 ) + { + wchar_t *term_line = wcsdupcat( L"term=", env_get( L"TERM" ) ); + wchar_t *term_line2 = wcsdup( term_line ); + wchar_t *mode_line = L"mode=emacs"; + wchar_t *app_line = L"fish"; + + wchar_t *term_line2_end = wcschr( term_line2, L'-' ); + if( term_line2_end ) + *term_line2_end=0; + + + cmd += wcslen( L"$if "); + while( *cmd == L' ' ) + cmd++; + + if( (wcscmp( cmd, app_line )==0) || + (wcscmp( cmd, term_line )==0) || + (wcscmp( cmd, mode_line )==0) ) + { +// fwprintf( stderr, L"Conditional %ls is true\n", cmd ); + inputrc_block_count++; + } + else + { +// fwprintf( stderr, L"Conditional %ls is false\n", cmd ); + inputrc_skip_block_count++; + } + free( term_line ); + free( term_line2 ); + + return; + } + debug( 1, L"I don\'t know what %ls means", cmd ); +} + +/** + Read the specified inputrc file +*/ +static void input_read_inputrc( wchar_t *fn ) +{ + FILE *rc; + wchar_t *buff=0; + int buff_len=0; + int error=0; +// fwprintf( stderr, L"read %ls\n", fn ); + + block(); + rc = wfopen( fn, "r" ); + + if( rc ) + { + while( !feof( rc ) && (!error)) + { + switch( fgetws2( &buff, &buff_len, rc ) ) + { + case -1: + { + debug( 1, + L"Error while reading input information from file: %s", + fn ); + + wperror( L"fgetws2 (read_ni)" ); + error=1; + break; + } + + default: + { + input_parse_inputrc_line( buff ); + + if( inputrc_error ) + { + fwprintf( stderr, L"%ls\n", buff ); + error=1; + } + } + } + } + free( buff ); + fclose( rc ); + } + unblock(); + + inputrc_skip_block_count=0; + inputrc_block_count=0; +} + +/** + Add a char * based character string mapping. +*/ +static void add_terminfo_mapping( const wchar_t *mode, + const char *seq, + const wchar_t *desc, + const wchar_t *func ) +{ + if( seq ) + { + wchar_t *tmp; + tmp=str2wcs(seq); + if( tmp ) + { + add_mapping( mode, tmp, desc, func ); + free(tmp); + } + } + +} + +static void add_escaped_mapping( const wchar_t *mode, + const wchar_t *seq, + const wchar_t *desc, + const wchar_t *func ) +{ + wchar_t *esc = input_expand_sequence( seq ); + if( esc ) + { + add_mapping( mode, esc, desc, func ); + free(esc); + } +} + + +static void add_common_bindings() +{ + static const wchar_t *name[] = + { + L"emacs", + L"vi", + L"vi-command" + } + ; + int i; + + /* + Universal bindings + */ + for( i=0; i<3; i++ ) + { + add_mapping( name[i], L"\e[A", L"Up", L"history-search-backward" ); + add_mapping( name[i], L"\e[B", L"Down", L"history-search-forward" ); + add_mapping( name[i], L"\e[C", L"Right", L"forward-char" ); + add_mapping( name[i], L"\e[D", L"Left", L"backward-char" ); + + add_terminfo_mapping( name[i], (key_left), L"Left", L"backward-char" ); + + add_terminfo_mapping( name[i], (key_right), L"Right", L"forward-char" ); + + add_terminfo_mapping( name[i], (key_up), L"Up", L"history-search-backward" ); + + add_terminfo_mapping( name[i], (key_down), L"Down", L"history-search-forward" ); + add_terminfo_mapping( name[i], (key_dc), L"Delete", L"delete-char" ); + + add_terminfo_mapping( name[i], (key_backspace), L"Backspace", L"backward-delete-char" ); + + add_mapping( name[i], L"\x7f", L"Backspace", L"backward-delete-char" ); + + add_terminfo_mapping( name[i], (key_home), L"Home", L"beginning-of-line" ); + + add_terminfo_mapping( name[i], (key_end), L"End", L"end-of-line" ); + + add_mapping( name[i], L"\e\eOC", L"Alt-Right", L"forward-word" ); + add_mapping( name[i], L"\e\eOD", L"Alt-Left", L"backward-word" ); + + add_mapping( name[i], L"\eO3C", L"Alt-Right", L"forward-word" ); + add_mapping( name[i], L"\eO3D", L"Alt-Left", L"backward-word" ); + + add_mapping( name[i], L"\e[3C", L"Alt-Right", L"forward-word" ); + add_mapping( name[i], L"\e[3D", L"Alt-Left", L"backward-word" ); + + add_mapping( name[i], L"\e\eOA", L"Alt-Up", L"history-token-search-backward" ); + add_mapping( name[i], L"\e\eOB", L"Alt-Down", L"history-token-search-forward" ); + + add_mapping( name[i], L"\eO3A", L"Alt-Up", L"history-token-search-backward" ); + add_mapping( name[i], L"\eO3B", L"Alt-Down", L"history-token-search-forward" ); + + add_mapping( name[i], L"\e[3A", L"Alt-Up", L"history-token-search-backward" ); + add_mapping( name[i], L"\e[3B", L"Alt-Down", L"history-token-search-forward" ); + + } + + /* + Bindings used in emacs and vi mode, but not in vi-command mode + */ + for( i=0; i<2; i++ ) + { + add_mapping( name[i], L"\t", L"Tab", L"complete" ); + add_escaped_mapping( name[i], (L"\\C-k"), L"Control-k", L"kill-line" ); + add_escaped_mapping( name[i], (L"\\C-y"), L"Control-y", L"yank" ); + add_mapping( name[i], L"", L"Any key", L"self-insert" ); + } +} + +static void add_emacs_bindings() +{ + add_escaped_mapping( L"emacs", (L"\\C-a"), L"Control-a", L"beginning-of-line" ); + add_escaped_mapping( L"emacs", (L"\\C-e"), L"Control-e", L"end-of-line" ); + add_escaped_mapping( L"emacs", (L"\\M-y"), L"Alt-y", L"yank-pop" ); + add_escaped_mapping( L"emacs", (L"\\C-h"), L"Control-h", L"backward-delete-char" ); + add_escaped_mapping( L"emacs", (L"\\C-e"), L"Control-e", L"end-of-line" ); + add_escaped_mapping( L"emacs", (L"\\C-w"), L"Control-w", L"backward-kill-word" ); + add_terminfo_mapping( L"emacs", (key_ppage), L"Page Up", L"beginning-of-history" ); + add_terminfo_mapping( L"emacs", (key_npage), L"Page Down", L"end-of-history" ); +} + +static void add_vi_bindings() +{ + add_mapping( L"vi", L"\e", L"Escape", L"bind -M vi-command" ); + + add_mapping( L"vi-command", L"i", L"i", L"bind -M vi" ); + add_mapping( L"vi-command", L"I", L"I", L"bind -M vi" ); + add_mapping( L"vi-command", L"k", L"k", L"history-search-backward" ); + add_mapping( L"vi-command", L"j", L"j", L"history-search-forward" ); + add_mapping( L"vi-command", L" ", L"Space", L"forward-char" ); + add_mapping( L"vi-command", L"l", L"l", L"forward-char" ); + add_mapping( L"vi-command", L"h", L"h", L"backward-char" ); + add_mapping( L"vi-command", L"$", L"$", L"end-of-line" ); + add_mapping( L"vi-command", L"^", L"^", L"beginning-of-line" ); + add_mapping( L"vi-command", L"0", L"0", L"beginning-of-line" ); + add_mapping( L"vi-command", L"b", L"b", L"backward-word" ); + add_mapping( L"vi-command", L"B", L"B", L"backward-word" ); + add_mapping( L"vi-command", L"w", L"w", L"forward-word" ); + add_mapping( L"vi-command", L"W", L"W", L"forward-word" ); + add_mapping( L"vi-command", L"x", L"x", L"delete-char" ); + +/* + movement ("h", "l"), word movement + ("b", "B", "w", "W", "e", "E"), moving to beginning and end of line + ("0", "^", "$"), and inserting and appending ("i", "I", "a", "A"), + changing and deleting ("c", "C", "d", "D"), character replacement and + deletion ("r", "x"), and finally yanking and pasting ("y", "p") +*/ + +} + +static int interrupt_handler() +{ + if( job_do_notification() ) + repaint(); + if( reader_interupted() ) + { + return 3; + } + return 0; +} + +int input_init() +{ + wchar_t *fn; + + input_common_init( &interrupt_handler ); + + if( setupterm( 0, STDOUT_FILENO, 0) == ERR ) + { + debug( 0, L"Could not set up terminal" ); + exit(1); + } + hash_init( &all_mappings, &hash_wcs_func, &hash_wcs_cmp ); + + /* + Add the default key bindings. + + Maybe some/most of these should be moved to the keybindings file? + */ + + /* + Many terminals (xterm, screen, etc.) have two different valid escape + sequences for arrow keys. One which is defined in terminfo/termcap + and one which is actually emitted by the arrow keys. The logic + escapes me, but I put in these hardcodes here for that reason. + */ + + add_common_bindings(); + add_emacs_bindings(); + add_vi_bindings(); + + current_mode_mappings = (array_list_t *)hash_get( &all_mappings, + L"emacs" ); + + + fn = env_get( L"INPUTRC" ); + + if( !fn ) + fn = L"~/.inputrc"; + + fn = expand_tilde( wcsdup( fn )); + + if( fn ) + { + input_read_inputrc( fn ); + free(fn); + } + + current_application_mappings = (array_list_t *)hash_get( &all_mappings, + L"fish" ); + global_mappings = (array_list_t *)hash_get( &all_mappings, + L"global" ); + + return 1; + +} + +static void destroy_mapping( const void *key, const void *val ) +{ + int i; + array_list_t *mappings = (array_list_t *)val; + + for( i=0; i<al_get_count( mappings ); i++ ) + { + mapping *m = (mapping *)al_get( mappings, i ); + free( m->seq ); + free( m->seq_desc ); + + free( m->command ); + free(m ); + } + + al_destroy( mappings ); + free((void *)key); + free((void *)val); +} + + +void input_destroy() +{ + input_common_destroy(); + + hash_foreach( &all_mappings, &destroy_mapping ); + hash_destroy( &all_mappings ); + + del_curterm( cur_term ); +} + + +static wint_t input_exec_binding( mapping *m, const wchar_t *seq ) +{ +// fwprintf( stderr, L"Binding %ls\n", m->command ); + wchar_t code = input_get_code( m->command ); + if( code != -1 ) + { + switch( code ) + { + case R_DUMP_FUNCTIONS: + { + dump_functions(); + return input_readch(); + } + case R_SELF_INSERT: + { + return seq[0]; + } + default: + return code; + } + } + else + { + + /* + This key sequence is bound to a command, which + is sent to the parser for evaluation. + */ + + /* + First clear the commandline. Do not issue a linebreak, since + many shortcut commands do not procuce output. + */ + write( 1, "\r", 1 ); + tputs(clr_eol,1,&writeb); + + reader_run_command( m->command ); + + /* + We still need to return something to the caller, R_NULL + tells the reader that nothing happened, but it might be a + godd idea to redraw and reexecute the prompt. + */ + return R_NULL; + } + +} + + + +/** + Try reading the specified function mapping +*/ + +static wint_t input_try_mapping( mapping *m) +{ + int j, k; + wint_t c=0; + + if( m->seq != 0 ) + { + for( j=0; m->seq[j] != L'\0' && + m->seq[j] == (c=input_common_readch( j>0 )); j++ ) + ; + if( m->seq[j] == L'\0' ) + { + + return input_exec_binding( m, m->seq ); + } + else + { + input_unreadch(c); + for(k=j-1; k>=0; k--) + input_unreadch(m->seq[k]); + } + } + return 0; + +} + +void input_unreadch( wint_t ch ) +{ + input_common_unreadch( ch ); +} + + +wint_t input_readch() +{ + + int i; + + /* + Clear the interrupted flag + */ + reader_interupted(); + + /* + Search for sequence in various mapping tables + */ + + while( 1 ) + { + + if( current_application_mappings ) + { + for( i=0; i<al_get_count( current_application_mappings); i++ ) + { + wint_t res = input_try_mapping( (mapping *)al_get( current_application_mappings, i )); + if( res ) + return res; + } + } + + if( global_mappings ) + { + for( i=0; i<al_get_count( global_mappings); i++ ) + { + wint_t res = input_try_mapping( (mapping *)al_get( global_mappings, i )); + if( res ) + return res; + } + } + + if( current_mode_mappings ) + { + for( i=0; i<al_get_count( current_mode_mappings); i++ ) + { + wint_t res = input_try_mapping( (mapping *)al_get( current_mode_mappings, i )); + if( res ) + return res; + } + } + + /* + No matching exact mapping, try to find the generic mapping. + */ + + for( i=0; i<al_get_count( current_mode_mappings); i++ ) + { + mapping *m = (mapping *)al_get( current_mode_mappings, i ); + if( wcslen( m->seq) == 0 ) + { + wchar_t arr[2]= + { + 0, + 0 + } + ; + arr[0] = input_common_readch(0); + + return input_exec_binding( m, arr ); + } + } + + input_common_readch( 0 ); + + } +} |