From 7483571f7fd62d1c4ba7f370f90e118342fa10d2 Mon Sep 17 00:00:00 2001 From: David Lawrence Ramsey Date: Sat, 4 Dec 2004 17:41:52 +0000 Subject: [PATCH] massive overhaul to support buffered input and output, the first steps toward proper wide character input and output, etc. git-svn-id: svn://svn.savannah.gnu.org/nano/trunk/nano@2157 35c25a1d-7b9e-4130-9fde-d3aeb78583b8 --- ChangeLog | 20 + src/files.c | 2 +- src/global.c | 16 +- src/nano.c | 449 ++++++++++++---- src/nano.h | 12 + src/proto.h | 36 +- src/rcfile.c | 3 + src/utils.c | 7 + src/winio.c | 1398 ++++++++++++++++++++++++++------------------------ 9 files changed, 1143 insertions(+), 800 deletions(-) diff --git a/ChangeLog b/ChangeLog index e95baa55..412cb1c5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -28,6 +28,26 @@ CVS code - by DLR) - Change references to "open files" to "open file buffers", for consistency. (DLR) + - Add option to disable UTF-8 sequence interpretation: + -O/--noutf8 on the command line, Meta-O as a toggle, and + "noutf8" in the rcfile. This is so people using single-byte + encodings such as KOI8-R can type properly again. (DLR, found + by Arthur Ivanov) + - Massively overhaul the input and output routines to support + buffered input and output, the first steps toward wide + character input and output, and double-Escape/verbatim input + of double-byte Unicode characters instead of single-byte ASCII + characters. New functions do_input(), do_mouse(), + do_output(), get_buffer(), get_buffer_len(), buffer_to_keys(), + unget_input(), get_input(), parse_kbinput(), and + parse_verbatim_kbinput(); new macro charcpy(); changes to + do_char() (renamed to do_output()), get_edit_input() (renamed + to do_input() and moved to nano.c), get_edit_mouse() (renamed + do_mouse() and moved to nano.c), do_verbatim_input(), + do_tab(), main(), and get_ascii_kbinput() (renamed to + get_word_kbinput()). (DLR; buffered input/output based on + ideas from mutt 1.4.2.1; double-Escape input of Unicode + characters suggested by Michael Piefel) - cut.c: do_cut_text() - If keep_cutbuffer is FALSE, only blow away the text in the diff --git a/src/files.c b/src/files.c index e725dc29..db2809fc 100644 --- a/src/files.c +++ b/src/files.c @@ -2583,7 +2583,7 @@ char *do_browser(const char *inpath) selected = numents - 1; else if (selectedbackup == selected) /* Put back the 'select' key */ - unget_kbinput('s', FALSE); + unget_kbinput('s', FALSE, FALSE); } else { /* Must be clicking a shortcut */ int mouse_x, mouse_y; diff --git a/src/global.c b/src/global.c index dd9c2ad4..4c4d2ec2 100644 --- a/src/global.c +++ b/src/global.c @@ -1074,15 +1074,19 @@ void toggle_init(void) #ifndef DISABLE_MOUSE toggle_init_one(TOGGLE_MOUSE_KEY, N_("Mouse support"), USE_MOUSE); #endif - /* If we're using restricted mode, the no-conversion and backup - * backup toggles are disabled. The former is useless since - * inserting files is disabled, and the latter is useless since - * backups are disabled. */ - if (!ISSET(RESTRICTED)) { + /* If we're using restricted mode, the DOS/Mac conversion toggle is + * disabled. It's useless since inserting files is disabled. */ + if (!ISSET(RESTRICTED)) toggle_init_one(TOGGLE_NOCONVERT_KEY, N_("No conversion from DOS/Mac format"), NO_CONVERT); +#ifdef NANO_WIDE + toggle_init_one(TOGGLE_NOUTF8_KEY, + N_("No conversion from UTF-8 format"), NO_UTF8); +#endif + /* If we're using restricted mode, the backup toggle is disabled. + * It's useless since backups are disabled. */ + if (!ISSET(RESTRICTED)) toggle_init_one(TOGGLE_BACKUP_KEY, N_("Backup files"), BACKUP_FILE); - } toggle_init_one(TOGGLE_SMOOTH_KEY, N_("Smooth scrolling"), SMOOTHSCROLL); toggle_init_one(TOGGLE_SMARTHOME_KEY, N_("Smart home key"), SMART_HOME); #ifdef ENABLE_COLOR diff --git a/src/nano.c b/src/nano.c index 9cdfc711..64c0d8e3 100644 --- a/src/nano.c +++ b/src/nano.c @@ -929,6 +929,9 @@ void usage(void) #ifndef NANO_SMALL print1opt("-N", "--noconvert", N_("Don't convert files from DOS/Mac format")); #endif +#ifdef NANO_WIDE + print1opt("-O", "--noutf8", N_("Don't convert files from UTF-8 format")); +#endif #ifndef DISABLE_JUSTIFY print1opt(_("-Q [str]"), _("--quotestr=[str]"), N_("Quoting string, default \"> \"")); #endif @@ -1146,87 +1149,20 @@ bool open_pipe(const char *command) } #endif /* !NANO_SMALL */ -/* The user typed a character; add it to the edit buffer. */ -void do_char(char ch) -{ - size_t current_len = strlen(current->data); -#if !defined(DISABLE_WRAPPING) || defined(ENABLE_COLOR) - bool do_refresh = FALSE; - /* Do we have to call edit_refresh(), or can we get away with - * update_line()? */ -#endif - - if (ch == '\0') /* Null to newline, if needed. */ - ch = '\n'; - else if (ch == '\n') { /* Newline to Enter, if needed. */ - do_enter(); - return; - } - - assert(current != NULL && current->data != NULL); - - /* When a character is inserted on the current magicline, it means - * we need a new one! */ - if (filebot == current) - new_magicline(); - - /* More dangerousness fun =) */ - current->data = charealloc(current->data, current_len + 2); - assert(current_x <= current_len); - charmove(¤t->data[current_x + 1], ¤t->data[current_x], - current_len - current_x + 1); - current->data[current_x] = ch; - totsize++; - set_modified(); - -#ifndef NANO_SMALL - /* Note that current_x has not yet been incremented. */ - if (current == mark_beginbuf && current_x < mark_beginx) - mark_beginx++; -#endif - - do_right(FALSE); - -#ifndef DISABLE_WRAPPING - /* If we're wrapping text, we need to call edit_refresh(). */ - if (!ISSET(NO_WRAP) && ch != '\t') - do_refresh = do_wrap(current); -#endif - -#ifdef ENABLE_COLOR - /* If color syntaxes are turned on, we need to call - * edit_refresh(). */ - if (ISSET(COLOR_SYNTAX)) - do_refresh = TRUE; -#endif - -#if !defined(DISABLE_WRAPPING) || defined(ENABLE_COLOR) - if (do_refresh) - edit_refresh(); - else -#endif - update_line(current, current_x); -} - void do_verbatim_input(void) { - int *v_kbinput = NULL; /* Used to hold verbatim input. */ - size_t v_len; /* Length of verbatim input. */ - size_t i; + int *kbinput; /* Used to hold verbatim input. */ + size_t kbinput_len; /* Length of verbatim input. */ statusbar(_("Verbatim input")); - v_kbinput = get_verbatim_kbinput(edit, &v_len, TRUE); + /* Read in all the verbatim characters. */ + kbinput = get_verbatim_kbinput(edit, &kbinput_len); - /* Turn on DISABLE_CURPOS while inserting character(s) and turn it - * off afterwards, so that if constant cursor position display is - * on, it will be updated properly. */ - SET(DISABLE_CURPOS); - for (i = 0; i < v_len; i++) - do_char((char)v_kbinput[i]); - UNSET(DISABLE_CURPOS); + /* Display all the verbatim characters at once. */ + do_output(kbinput, kbinput_len); - free(v_kbinput); + free(kbinput); } void do_backspace(void) @@ -1315,7 +1251,8 @@ void do_delete(void) void do_tab(void) { - do_char('\t'); + int kbinput = '\t'; + do_output(&kbinput, 1); } /* Someone hits return *gasp!* */ @@ -2753,7 +2690,7 @@ void do_justify(bool full_justify) size_t mark_beginx_save = mark_beginx; #endif int kbinput; - bool meta_key, func_key; + bool meta_key, func_key, s_or_t; /* If we're justifying the entire file, start at the beginning. */ if (full_justify) @@ -3019,9 +2956,10 @@ void do_justify(bool full_justify) /* Now get a keystroke and see if it's unjustify. If not, put back * the keystroke and return. */ - kbinput = get_edit_input(&meta_key, &func_key, FALSE); + kbinput = do_input(&meta_key, &func_key, &s_or_t, FALSE); - if (!meta_key && !func_key && kbinput == NANO_UNJUSTIFY_KEY) { + if (!meta_key && !func_key && s_or_t && + kbinput == NANO_UNJUSTIFY_KEY) { /* Restore the justify we just did (ungrateful user!). */ current = current_save; current_x = current_x_save; @@ -3073,7 +3011,7 @@ void do_justify(bool full_justify) edit_refresh(); } } else { - unget_kbinput(kbinput, meta_key); + unget_kbinput(kbinput, meta_key, func_key); /* Blow away the text in the justify buffer. */ free_filestruct(jusbuffer); @@ -3418,6 +3356,322 @@ void terminal_init(void) disable_flow_control(); } +int do_input(bool *meta_key, bool *func_key, bool *s_or_t, bool + allow_funcs) +{ + int input; + /* The character we read in. */ + static int *kbinput = NULL; + /* The input buffer. */ + static size_t kbinput_len = 0; + /* The length of the input buffer. */ + const shortcut *s; + bool have_shortcut; +#ifndef NANO_SMALL + const toggle *t; + bool have_toggle; +#endif + + *s_or_t = FALSE; + + /* Read in a character. */ + input = get_kbinput(edit, meta_key, func_key); + +#ifndef DISABLE_MOUSE + /* If we got a mouse click and it was on a shortcut, read in the + * shortcut character. */ + if (allow_funcs && func_key && input == KEY_MOUSE) { + if (do_mouse()) + input = get_kbinput(edit, meta_key, func_key); + else + input = ERR; + } +#endif + + /* Check for a shortcut in the main list. */ + s = get_shortcut(main_list, &input, meta_key, func_key); + + /* If we got a shortcut from the main list, or a "universal" + * edit window shortcut, set have_shortcut to TRUE. */ + have_shortcut = (s != NULL || input == NANO_XON_KEY || + input == NANO_XOFF_KEY || input == NANO_SUSPEND_KEY); + +#ifndef NANO_SMALL + /* Check for a toggle in the main list. */ + t = get_toggle(input, *meta_key); + + /* If we got a toggle from the main list, set have_toggle to + * TRUE. */ + have_toggle = (t != NULL); +#endif + + /* Set s_or_t to TRUE if we got a shortcut or toggle. */ + *s_or_t = (have_shortcut +#ifndef NANO_SMALL + || have_toggle +#endif + ); + + if (allow_funcs) { + /* If we got a character, and it isn't a shortcut, toggle, or + * control character, it's a normal text character. Display the + * warning if we're in view mode, or add the character to the + * input buffer if we're not. */ + if (input != ERR && *s_or_t == FALSE && !is_cntrl_char(input)) { + if (ISSET(VIEW_MODE)) + print_view_warning(); + else { + kbinput_len++; + kbinput = (int *)nrealloc(kbinput, kbinput_len * + sizeof(int)); + kbinput[kbinput_len - 1] = input; + } + } + + /* If we got a shortcut or toggle, or if there aren't any other + * characters waiting after the one we read in, we need to + * display all the characters in the input buffer if it isn't + * empty. Note that it should be empty if we're in view + * mode. */ + if (*s_or_t == TRUE || get_buffer_len() == 0) { + if (kbinput != NULL) { + /* Display all the characters in the input buffer at + * once. */ + do_output(kbinput, kbinput_len); + + /* Empty the input buffer. */ + kbinput_len = 0; + free(kbinput); + kbinput = NULL; + } + } + + if (have_shortcut) { + switch (input) { + /* Handle the "universal" edit window shortcuts. */ + case NANO_XON_KEY: + statusbar(_("XON ignored, mumble mumble.")); + break; + case NANO_XOFF_KEY: + statusbar(_("XOFF ignored, mumble mumble.")); + break; +#ifndef NANO_SMALL + case NANO_SUSPEND_KEY: + if (ISSET(SUSPEND)) + do_suspend(0); + break; +#endif + /* Handle the normal edit window shortcuts. */ + default: + /* Blow away the text in the cutbuffer if we aren't + * cutting text. */ + if (s->func != do_cut_text) + cutbuffer_reset(); + + /* Run the function associated with this shortcut, + * if there is one. */ + if (s->func != NULL) { + if (ISSET(VIEW_MODE) && !s->viewok) + print_view_warning(); + else + s->func(); + } + break; + } + } +#ifndef NANO_SMALL + else if (have_toggle) { + /* Blow away the text in the cutbuffer, since we aren't + * cutting text. */ + cutbuffer_reset(); + /* Toggle the flag associated with this shortcut. */ + if (allow_funcs) + do_toggle(t); + } +#endif + else + /* Blow away the text in the cutbuffer, since we aren't + * cutting text. */ + cutbuffer_reset(); + } + + return input; +} + +#ifndef DISABLE_MOUSE +bool do_mouse(void) +{ + int mouse_x, mouse_y; + bool retval; + + retval = get_mouseinput(&mouse_x, &mouse_y, TRUE); + + if (!retval) { + /* We can click in the edit window to move the cursor. */ + if (wenclose(edit, mouse_y, mouse_x)) { + bool sameline; + /* Did they click on the line with the cursor? If they + * clicked on the cursor, we set the mark. */ + size_t xcur; + /* The character they clicked on. */ + + /* Subtract out the size of topwin. Perhaps we need a + * constant somewhere? */ + mouse_y -= 2; + + sameline = (mouse_y == current_y); + + /* Move to where the click occurred. */ + for (; current_y < mouse_y && current->next != NULL; current_y++) + current = current->next; + for (; current_y > mouse_y && current->prev != NULL; current_y--) + current = current->prev; + + xcur = actual_x(current->data, get_page_start(xplustabs()) + + mouse_x); + +#ifndef NANO_SMALL + /* Clicking where the cursor is toggles the mark, as does + * clicking beyond the line length with the cursor at the + * end of the line. */ + if (sameline && xcur == current_x) { + if (ISSET(VIEW_MODE)) { + print_view_warning(); + return retval; + } + do_mark(); + } +#endif + + current_x = xcur; + placewewant = xplustabs(); + edit_refresh(); + } + } + /* FIXME: If we clicked on a location in the statusbar, the cursor + * should move to the location we clicked on. This functionality + * should be in do_statusbar_mouse() when it's written. */ + + return retval; +} +#endif /* !DISABLE_MOUSE */ + +/* The user typed kbinput_len characters. Add them to the edit + * buffer. */ +void do_output(int *kbinput, size_t kbinput_len) +{ + size_t i, current_len = strlen(current->data); + bool old_constupdate = ISSET(CONSTUPDATE); + bool do_refresh = FALSE; + /* Do we have to call edit_refresh(), or can we get away with + * update_line()? */ + + char key[ +#ifdef NANO_WIDE + MB_LEN_MAX +#else + 1 +#endif + ]; /* The current character we have. */ + int key_len; /* The length of the current character. */ + + assert(current != NULL && current->data != NULL); + + /* Turn off constant cursor position display if it's on. */ + if (old_constupdate) + UNSET(CONSTUPDATE); + + for (i = 0; i < kbinput_len; i++) { +#ifdef NANO_WIDE + /* Change the wide character to its multibyte value. If it's + * invalid, go on to the next character. */ + if (!ISSET(NO_UTF8)) { + key_len = wctomb(key, kbinput[i]); + + if (key_len == -1) + continue; + } else { +#endif + /* Interpret the character as a single-byte sequence. */ + key_len = 1; + key[0] = (char)kbinput[i]; +#ifdef NANO_WIDE + } +#endif + + /* Null to newline, if needed. */ + if (key[0] == '\0' && key_len == 1) + key[0] = '\n'; + /* Newline to Enter, if needed. */ + else if (key[0] == '\n' && key_len == 1) { + do_enter(); + continue; + } + + /* When a character is inserted on the current magicline, it + * means we need a new one! */ + if (filebot == current) + new_magicline(); + + /* More dangerousness fun =) */ + current->data = charealloc(current->data, + current_len + key_len + 1); + assert(current_x <= current_len); + charmove(¤t->data[current_x + key_len], + ¤t->data[current_x], + current_len - current_x + key_len); + charcpy(¤t->data[current_x], key, key_len); + current_len += key_len; + /* FIXME: Should totsize be the number of single-byte characters + * or the number of multibyte characters? Assume for former for + * now. */ + totsize += key_len; + set_modified(); + +#ifndef NANO_SMALL + /* Note that current_x has not yet been incremented. */ + if (current == mark_beginbuf && current_x < mark_beginx) + mark_beginx += key_len; +#endif + + { + int j; + for (j = 0; j < key_len; j++) + do_right(FALSE); + } + +#ifndef DISABLE_WRAPPING + /* If we're wrapping text, we need to call edit_refresh(). */ + if (!ISSET(NO_WRAP) && (key[0] != '\t' || key_len != 1)) { + bool do_refresh_save = do_refresh; + + do_refresh = do_wrap(current); + + /* If we needed to call edit_refresh() before this, we'll + * still need to after this. */ + if (do_refresh_save) + do_refresh = TRUE; + } +#endif + +#ifdef ENABLE_COLOR + /* If color syntaxes are turned on, we need to call + * edit_refresh(). */ + if (ISSET(COLOR_SYNTAX)) + do_refresh = TRUE; +#endif + } + + /* Turn constant cursor position display back on if it was on. */ + if (old_constupdate) + SET(CONSTUPDATE); + + if (do_refresh) + edit_refresh(); + else + update_line(current, current_x); +} + int main(int argc, char **argv) { int optchr; @@ -3432,9 +3686,6 @@ int main(int argc, char **argv) /* The old value of the multibuffer option, restored after we * load all files on the command line. */ #endif - int kbinput; - /* Input from keyboard. */ - bool meta_key, func_key; #ifdef HAVE_GETOPT_LONG const struct option long_options[] = { {"help", 0, 0, 'h'}, @@ -3447,6 +3698,9 @@ int main(int argc, char **argv) #endif {"ignorercfiles", 0, 0, 'I'}, #endif +#ifdef NANO_WIDE + {"noutf8", 0, 0, 'O'}, +#endif #ifndef DISABLE_JUSTIFY {"quotestr", 1, 0, 'Q'}, #endif @@ -3510,9 +3764,9 @@ int main(int argc, char **argv) while ((optchr = #ifdef HAVE_GETOPT_LONG - getopt_long(argc, argv, "h?ABE:FHINQ:RST:VY:Zabcdefgijklmo:pr:s:tvwxz", long_options, NULL) + getopt_long(argc, argv, "h?ABE:FHINOQ:RST:VY:Zabcdefgijklmo:pr:s:tvwxz", long_options, NULL) #else - getopt(argc, argv, "h?ABE:FHINQ:RST:VY:Zabcdefgijklmo:pr:s:tvwxz") + getopt(argc, argv, "h?ABE:FHINOQ:RST:VY:Zabcdefgijklmo:pr:s:tvwxz") #endif ) != -1) { @@ -3556,6 +3810,9 @@ int main(int argc, char **argv) SET(NO_CONVERT); break; #endif + case 'O': + SET(NO_UTF8); + break; #ifndef DISABLE_JUSTIFY case 'Q': quotestr = mallocstrcpy(quotestr, optarg); @@ -3929,7 +4186,18 @@ int main(int argc, char **argv) edit_refresh(); while (TRUE) { + bool meta_key; + /* Whether we got a meta key sequence. */ + bool func_key; + /* Whether we got a function key. */ + bool s_or_t; + /* Whether we got a shortcut or toggle. */ + + /* Make sure the cursor is in the edit window. */ reset_cursor(); + + /* If constant cursor position display is on, display the cursor + * position. */ if (ISSET(CONSTUPDATE)) do_cursorpos(TRUE); @@ -3937,17 +4205,8 @@ int main(int argc, char **argv) currshortcut = main_list; #endif - kbinput = get_edit_input(&meta_key, &func_key, TRUE); - - /* Last gasp, stuff that's not in the main lists. */ - if (kbinput != ERR && !is_cntrl_char(kbinput)) { - /* Don't stop unhandled printable sequences, so that people - * with odd character sets can type. */ - if (ISSET(VIEW_MODE)) - print_view_warning(); - else - do_char(kbinput); - } + /* Read in and interpret characters. */ + do_input(&meta_key, &func_key, &s_or_t, TRUE); } assert(FALSE); } diff --git a/src/nano.h b/src/nano.h index c8e0d12a..9a217f26 100644 --- a/src/nano.h +++ b/src/nano.h @@ -44,6 +44,7 @@ #define charalloc(howmuch) (char *)nmalloc((howmuch) * sizeof(char)) #define charealloc(ptr, howmuch) (char *)nrealloc(ptr, (howmuch) * sizeof(char)) #define charmove(dest, src, n) memmove(dest, src, (n) * sizeof(char)) +#define charcpy(dest, src, n) memcpy(dest, src, (n) * sizeof(char)) #ifdef BROKEN_REGEXEC #define regexec(preg, string, nmatch, pmatch, eflags) regexec_safe(preg, string, nmatch, pmatch, eflags) @@ -134,6 +135,10 @@ #define VERMSG "GNU nano " VERSION +/* FIXME: We should be checking for this instead of unconditionally + * using it. */ +#define NANO_WIDE 1 + /* If we aren't using ncurses, turn the mouse support off, as it's * ncurses-specific. */ #ifndef NCURSES_MOUSE_VERSION @@ -158,6 +163,11 @@ typedef enum { } topmidnone; /* Structure types. */ +typedef struct buffer { + int key; + bool key_code; +} buffer; + typedef struct filestruct { char *data; struct filestruct *next; /* Next node. */ @@ -317,6 +327,7 @@ typedef struct historyheadtype { #define RESTRICTED (1<<26) #define SMART_HOME (1<<27) #define WHITESPACE_DISPLAY (1<<28) +#define NO_UTF8 (1<<29) /* Control key sequences, changing these would be very very bad. */ #define NANO_CONTROL_SPACE 0 @@ -501,6 +512,7 @@ typedef struct historyheadtype { #define TOGGLE_SYNTAX_KEY NANO_ALT_Y #define TOGGLE_SMARTHOME_KEY NANO_ALT_H #define TOGGLE_WHITESPACE_KEY NANO_ALT_P +#define TOGGLE_NOUTF8_KEY NANO_ALT_O #endif /* !NANO_SMALL */ #define MAIN_VISIBLE 12 diff --git a/src/proto.h b/src/proto.h index 18ea60dd..e4709915 100644 --- a/src/proto.h +++ b/src/proto.h @@ -327,7 +327,6 @@ void nano_disabled_msg(void); RETSIGTYPE cancel_fork(int signal); bool open_pipe(const char *command); #endif -void do_char(char ch); void do_verbatim_input(void); void do_backspace(void); void do_delete(void); @@ -391,6 +390,12 @@ void enable_signals(void); void disable_flow_control(void); void enable_flow_control(void); void terminal_init(void); +int do_input(bool *meta_key, bool *func_key, bool *s_or_t, bool + allow_funcs); +#ifndef DISABLE_MOUSE +bool do_mouse(void); +#endif +void do_output(int *kbinput, size_t kbinput_len); /* Public functions in rcfile.c. */ #ifdef ENABLE_NANORC @@ -468,6 +473,7 @@ int regexp_bol_or_eol(const regex_t *preg, const char *string); int is_blank_char(int c); #endif int is_cntrl_char(int c); +bool is_byte_char(int c); int num_of_digits(int n); bool parse_num(const char *str, ssize_t *val); void align(char **strp); @@ -524,29 +530,29 @@ int check_wildcard_match(const char *text, const char *pattern); #ifndef NANO_SMALL void reset_kbinput(void); #endif -void unget_kbinput(int kbinput, bool meta_key); +void get_buffer(WINDOW *win); +size_t get_buffer_len(void); +int *buffer_to_keys(buffer *input, size_t input_len); +void unget_input(buffer *input, size_t input_len); +void unget_kbinput(int kbinput, bool meta_key, bool func_key); +buffer *get_input(WINDOW *win, size_t input_len); int get_kbinput(WINDOW *win, bool *meta_key, bool *func_key); -int get_translated_kbinput(int kbinput, bool *escape_seq -#ifndef NANO_SMALL - , bool reset -#endif - ); -int get_ascii_kbinput(int kbinput, int ascii_digits +int parse_kbinput(WINDOW *win, bool *meta_key, bool *func_key #ifndef NANO_SMALL , bool reset #endif ); -int get_control_kbinput(int kbinput); -int get_escape_seq_kbinput(const int *escape_seq, size_t es_len, bool +int get_escape_seq_kbinput(const int *sequence, size_t seq_len, bool *ignore_seq); int get_escape_seq_abcd(int kbinput); -int *get_verbatim_kbinput(WINDOW *win, size_t *v_len, bool allow_ascii); -int get_untranslated_kbinput(int kbinput, size_t position, bool - allow_ascii +int get_word_kbinput(int kbinput #ifndef NANO_SMALL , bool reset #endif ); +int get_control_kbinput(int kbinput); +int *get_verbatim_kbinput(WINDOW *win, size_t *kbinput_len); +int *parse_verbatim_kbinput(WINDOW *win, size_t *kbinput_len); #ifndef DISABLE_MOUSE bool get_mouseinput(int *mouse_x, int *mouse_y, bool allow_shortcuts); #endif @@ -555,10 +561,6 @@ const shortcut *get_shortcut(const shortcut *s_list, int *kbinput, bool #ifndef NANO_SMALL const toggle *get_toggle(int kbinput, bool meta_key); #endif -int get_edit_input(bool *meta_key, bool *func_key, bool allow_funcs); -#ifndef DISABLE_MOUSE -bool get_edit_mouse(void); -#endif size_t xplustabs(void); size_t actual_x(const char *str, size_t xplus); size_t strnlenpt(const char *buf, size_t size); diff --git a/src/rcfile.c b/src/rcfile.c index 8f1780cb..a02805e8 100644 --- a/src/rcfile.c +++ b/src/rcfile.c @@ -68,6 +68,9 @@ const static rcoption rcopts[] = { #endif {"nofollow", NOFOLLOW_SYMLINKS}, {"nohelp", NO_HELP}, +#ifdef NANO_WIDE + {"noutf8", NO_UTF8}, +#endif #ifndef DISABLE_WRAPPING {"nowrap", NO_WRAP}, #endif diff --git a/src/utils.c b/src/utils.c index da494e0f..bda6ce74 100644 --- a/src/utils.c +++ b/src/utils.c @@ -70,6 +70,13 @@ int is_cntrl_char(int c) (127 <= c && c < 160); } +/* Return TRUE if the character c is in byte range, and FALSE + * otherwise. */ +bool is_byte_char(int c) +{ + return (unsigned int)c == (unsigned char)c; +} + int num_of_digits(int n) { int i = 1; diff --git a/src/winio.c b/src/winio.c index 60866bb7..c3b810bc 100644 --- a/src/winio.c +++ b/src/winio.c @@ -32,7 +32,14 @@ #include "proto.h" #include "nano.h" -static int statusblank = 0; /* Number of keystrokes left after +static buffer *key_buffer = NULL; + /* The default keystroke buffer, + * containing all the keystrokes we have + * at a given point. */ +static size_t key_buffer_len = 0; + /* The length of the default keystroke + * buffer. */ +static int statusblank = 0; /* The number of keystrokes left after * we call statusbar(), before we * actually blank the statusbar. */ static bool resetstatuspos = FALSE; @@ -105,446 +112,565 @@ static bool resetstatuspos = FALSE; /* Reset all the input routines that rely on character sequences. */ void reset_kbinput(void) { - get_translated_kbinput(0, NULL, TRUE); - get_ascii_kbinput(0, 0, TRUE); - get_untranslated_kbinput(0, 0, FALSE, TRUE); +#ifdef NANO_WIDE + /* Reset the multibyte and wide character interpreter states. */ + if (!ISSET(NO_UTF8)) { + mbtowc(NULL, NULL, 0); + wctomb(NULL, 0); + } +#endif + parse_kbinput(NULL, NULL, NULL, TRUE); + get_word_kbinput(0, TRUE); } #endif -/* Put back the input character stored in kbinput. If meta_key is TRUE, - * put back the Escape character after putting back kbinput. */ -void unget_kbinput(int kbinput, bool meta_key) +/* Read in a sequence of keystrokes from win and save them in the + * default keystroke buffer. This should only be called when the + * default keystroke buffer is empty. */ +void get_buffer(WINDOW *win) { - ungetch(kbinput); - if (meta_key) - ungetch(NANO_CONTROL_3); -} + int input; + size_t i = 0; -/* Read in a single input character. If it's ignored, swallow it and go - * on. Otherwise, try to translate it from ASCII, extended keypad - * values, and/or escape sequences. Set meta_key to TRUE when we get a - * meta sequence, and set func_key to TRUE when we get an extended - * keypad sequence. Supported extended keypad values consist of [arrow - * key], Ctrl-[arrow key], Shift-[arrow key], Enter, Backspace, the - * editing keypad (Insert, Delete, Home, End, PageUp, and PageDown), the - * function keypad (F1-F16), and the numeric keypad with NumLock off. - * Assume nodelay(win) is FALSE. */ -int get_kbinput(WINDOW *win, bool *meta_key, bool *func_key) -{ - int kbinput, retval = ERR; - bool escape_seq; +#ifdef NANO_WIDE + size_t wide_key_buffer_len = 0; + buffer *wide_key_buffer = NULL; +#endif + + /* If the keystroke buffer isn't empty, get out. */ + if (key_buffer != NULL) + return; + + /* Read in the first character using blocking input. */ + nodelay(win, FALSE); #ifndef NANO_SMALL allow_pending_sigwinch(TRUE); #endif + input = wgetch(win); +#ifndef NANO_SMALL + allow_pending_sigwinch(FALSE); +#endif - *meta_key = FALSE; - *func_key = FALSE; + /* Increment the length of the keystroke buffer, save the value of + * the keystroke in key, and set key_code to TRUE if the keystroke + * is an extended keypad value and hence shouldn't be treated as a + * multibyte character. */ + key_buffer_len++; + key_buffer = (buffer *)nmalloc(sizeof(buffer)); + key_buffer[0].key = input; + key_buffer[0].key_code = !is_byte_char(input); - while (retval == ERR) { - /* Read a character using blocking input, since using - * non-blocking input will eat up all unused CPU. Then pass it - * to get_translated_kbinput(). Continue until we get a - * complete sequence. */ - kbinput = wgetch(win); - retval = get_translated_kbinput(kbinput, &escape_seq + /* Read in the remaining characters using non-blocking input. */ + nodelay(win, TRUE); + + while (TRUE) { #ifndef NANO_SMALL - , FALSE + allow_pending_sigwinch(TRUE); #endif - ); + input = wgetch(win); +#ifndef NANO_SMALL + allow_pending_sigwinch(FALSE); +#endif + + /* If there aren't any more characters, stop reading. */ + if (input == ERR) + break; + + /* Otherwise, increment the length of the keystroke buffer, save + * the value of the keystroke in key, and set key_code to TRUE + * if the keystroke is an extended keypad value and hence + * shouldn't be treated as a multibyte character. */ + key_buffer_len++; + key_buffer = (buffer *)nrealloc(key_buffer, key_buffer_len * + sizeof(buffer)); + key_buffer[key_buffer_len - 1].key = input; + key_buffer[key_buffer_len - 1].key_code = !is_byte_char(input); + } + + /* Switch back to non-blocking input. */ + nodelay(win, FALSE); - /* If we got a complete non-escape sequence, interpret the - * translated value instead of the value of the last character - * we got. Set func_key to TRUE if the translated value is - * outside byte range. */ - if (retval != ERR) { - *func_key = (retval > 255); - kbinput = retval; +#ifdef NANO_WIDE + if (!ISSET(NO_UTF8)) { + /* Change all incomplete or invalid multibyte keystrokes to -1, + * and change all complete and valid multibyte keystrokes to + * their wide character value. */ + for (i = 0; i < key_buffer_len; i++) { + wchar_t wide_key; + + if (!key_buffer[i].key_code) { + key_buffer[i].key = mbtowc(&wide_key, + (const char *)&key_buffer[i].key, 1); + if (key_buffer[i].key != -1) + key_buffer[i].key = wide_key; + } } - /* If we got an incomplete escape sequence, put back the initial - * non-escape and then read the entire sequence in as verbatim - * input. */ - if (escape_seq) { - int *sequence; - size_t seq_len; - - unget_kbinput(kbinput, FALSE); - sequence = get_verbatim_kbinput(win, &seq_len, FALSE); - - /* If the escape sequence is one character long, set - * meta_key to TRUE, make the sequence character lowercase, - * and save that as the result. */ - if (seq_len == 1) { - *meta_key = TRUE; - retval = tolower(kbinput); - /* If the escape sequence is more than one character long, - * set func_key to TRUE, translate the escape sequence into - * the corresponding key value, and save that as the - * result. */ - } else if (seq_len > 1) { - bool ignore_seq; - - *func_key = TRUE; - retval = get_escape_seq_kbinput(sequence, seq_len, - &ignore_seq); - - if (retval == ERR && !ignore_seq) { - /* This escape sequence is unrecognized. Send it - * back. */ - for (; seq_len > 1; seq_len--) - unget_kbinput(sequence[seq_len - 1], FALSE); - retval = sequence[0]; - } + /* Save all of the non-(-1) keystrokes in another buffer. */ + for (i = 0; i < key_buffer_len; i++) { + if (key_buffer[i].key != -1) { + wide_key_buffer_len++; + wide_key_buffer = (buffer *)nrealloc(wide_key_buffer, + wide_key_buffer_len * sizeof(buffer)); + + wide_key_buffer[wide_key_buffer_len - 1].key = + key_buffer[i].key; + wide_key_buffer[wide_key_buffer_len - 1].key_code = + key_buffer[i].key_code; } - free(sequence); } + + /* Replace the default keystroke buffer with the non-(-1) + * keystroke buffer. */ + key_buffer_len = wide_key_buffer_len; + free(key_buffer); + key_buffer = wide_key_buffer; } +#endif +} -#ifdef DEBUG - fprintf(stderr, "get_kbinput(): kbinput = %d, meta_key = %d, func_key = %d\n", kbinput, (int)*meta_key, (int)*func_key); +/* Return the length of the default keystroke buffer. */ +size_t get_buffer_len(void) +{ + return key_buffer_len; +} + +/* Return the key values stored in the keystroke buffer input, + * discarding the key_code values in it. */ +int *buffer_to_keys(buffer *input, size_t input_len) +{ + int *sequence = (int *)nmalloc(input_len * sizeof(int)); + size_t i; + + for (i = 0; i < input_len; i++) + sequence[i] = input[i].key; + + return sequence; +} + +/* Add the contents of the keystroke buffer input to the default + * keystroke buffer. */ +void unget_input(buffer *input, size_t input_len) +{ +#ifndef NANO_SMALL + allow_pending_sigwinch(TRUE); + allow_pending_sigwinch(FALSE); #endif + /* If input is empty, get out. */ + if (input_len == 0) + return; + + /* If adding input would put the default keystroke buffer beyond + * maximum capacity, only add enough of input to put it at maximum + * capacity. */ + if (key_buffer_len + input_len < key_buffer_len) + input_len = (size_t)-1 - key_buffer_len; + + /* Add the length of input to the length of the default keystroke + * buffer, and reallocate the default keystroke buffer so that it + * has enough room for input. */ + key_buffer_len += input_len; + key_buffer = (buffer *)nrealloc(key_buffer, key_buffer_len * + sizeof(buffer)); + + /* If the default keystroke buffer wasn't empty before, move its + * beginning forward far enough so that we can add input to its + * beginning. */ + if (key_buffer_len > input_len) + memmove(key_buffer + input_len, key_buffer, + (key_buffer_len - input_len) * sizeof(buffer)); + + /* Copy input to the beginning of the default keystroke buffer. */ + memcpy(key_buffer, input, input_len * sizeof(buffer)); +} + +/* Put back the character stored in kbinput. If func_key is TRUE and + * the character is out of byte range, interpret it as an extended + * keypad value. If meta_key is TRUE, put back the Escape character + * after putting back kbinput. */ +void unget_kbinput(int kbinput, bool meta_key, bool func_key) +{ + buffer input; + + input.key = kbinput; + input.key_code = (func_key && !is_byte_char(kbinput)); + + unget_input(&input, 1); + + if (meta_key) { + input.key = NANO_CONTROL_3; + input.key_code = FALSE; + unget_input(&input, 1); + } +} + +/* Try to read input_len characters from the default keystroke buffer. + * If the default keystroke buffer is empty and win isn't NULL, try to + * read in more characters from win and add them to the default + * keystroke buffer before doing anything else. If the default + * keystroke buffer is empty and win is NULL, return NULL. */ +buffer *get_input(WINDOW *win, size_t input_len) +{ + buffer *input; + #ifndef NANO_SMALL + allow_pending_sigwinch(TRUE); allow_pending_sigwinch(FALSE); #endif - return retval; + if (key_buffer_len == 0) { + if (win != NULL) + get_buffer(win); + + if (key_buffer_len == 0) + return NULL; + } + + /* If input_len is greater than the length of the default keystroke + * buffer, only read the number of characters in the default + * keystroke buffer. */ + if (input_len > key_buffer_len) + input_len = key_buffer_len; + + /* Subtract input_len from the length of the default keystroke + * buffer, and allocate the keystroke buffer input so that it + * has enough room for input_len keystrokes. */ + key_buffer_len -= input_len; + input = (buffer *)nmalloc(input_len * sizeof(buffer)); + + /* Copy input_len characters from the beginning of the default + * keystroke buffer into input. */ + memcpy(input, key_buffer, input_len * sizeof(buffer)); + + /* If the default keystroke buffer is empty, mark it as such. */ + if (key_buffer_len == 0) { + free(key_buffer); + key_buffer = NULL; + /* If the default keystroke buffer isn't empty, move its + * beginning forward far enough back so that the keystrokes in input + * are no longer at its beginning. */ + } else { + memmove(key_buffer, key_buffer + input_len, key_buffer_len * + sizeof(buffer)); + key_buffer = (buffer *)nrealloc(key_buffer, key_buffer_len * + sizeof(buffer)); + } + + return input; } -/* Translate acceptable ASCII, extended keypad values, and escape - * sequences into their corresponding key values. Set escape_seq to - * TRUE when we get an incomplete escape sequence. Assume nodelay(win) - * is FALSE. */ -int get_translated_kbinput(int kbinput, bool *escape_seq +/* Read in a single character. If it's ignored, swallow it and go on. + * Otherwise, try to translate it from ASCII, meta key sequences, escape + * sequences, and/or extended keypad values. Set meta_key to TRUE when + * we get a meta key sequence, and set func_key to TRUE when we get an + * extended keypad value. Supported extended keypad values consist of + * [arrow key], Ctrl-[arrow key], Shift-[arrow key], Enter, Backspace, + * the editing keypad (Insert, Delete, Home, End, PageUp, and PageDown), + * the function keypad (F1-F16), and the numeric keypad with NumLock + * off. Assume nodelay(win) is FALSE. */ +int get_kbinput(WINDOW *win, bool *meta_key, bool *func_key) +{ + int kbinput; + + /* Read in a character and interpret it. Continue doing this until + * we get a recognized value or sequence. */ + while ((kbinput = parse_kbinput(win, meta_key, func_key +#ifndef NANO_SMALL + , FALSE +#endif + )) == ERR); + + return kbinput; +} + +/* Translate ASCII characters, extended keypad values, and escape + * sequences into their corresponding key values. Set meta_key to TRUE + * when we get a meta key sequence, and set func_key to TRUE when we get + * a function key. Assume nodelay(win) is FALSE. */ +int parse_kbinput(WINDOW *win, bool *meta_key, bool *func_key #ifndef NANO_SMALL , bool reset #endif ) + { static int escapes = 0; - static int ascii_digits = 0; + static int word_digits = 0; + buffer *kbinput; int retval = ERR; + bool no_func_key = FALSE; -#ifndef NANO_SMALL if (reset) { escapes = 0; - ascii_digits = 0; + word_digits = 0; return ERR; } -#endif - *escape_seq = FALSE; + *meta_key = FALSE; + *func_key = FALSE; - switch (kbinput) { - case ERR: - break; - case NANO_CONTROL_3: - /* Increment the escape counter. */ - escapes++; - switch (escapes) { - case 1: - /* One escape: wait for more input. */ - case 2: - /* Two escapes: wait for more input. */ - break; - default: - /* More than two escapes: reset the escape counter - * and wait for more input. */ - escapes = 0; - } - break; + /* Read in a character. */ + while ((kbinput = get_input(win, 1)) == NULL); + + /* If we got an extended keypad value or an ASCII character, + * translate it. */ + if (kbinput->key_code || is_byte_char(kbinput->key)) { + switch (kbinput->key) { + case ERR: + break; + case NANO_CONTROL_3: + /* Increment the escape counter. */ + escapes++; + switch (escapes) { + case 1: + /* One escape: wait for more input. */ + case 2: + /* Two escapes: wait for more input. */ + break; + default: + /* More than two escapes: reset the escape + * counter and wait for more input. */ + escapes = 0; + } + break; #if !defined(NANO_SMALL) && defined(KEY_RESIZE) - /* Since we don't change the default SIGWINCH handler when - * NANO_SMALL is defined, KEY_RESIZE is never generated. Also, - * Slang and SunOS 5.7-5.9 don't support KEY_RESIZE. */ - case KEY_RESIZE: - break; + /* Since we don't change the default SIGWINCH handler when + * NANO_SMALL is defined, KEY_RESIZE is never generated. + * Also, Slang and SunOS 5.7-5.9 don't support + * KEY_RESIZE. */ + case KEY_RESIZE: + break; #endif #ifdef PDCURSES - case KEY_SHIFT_L: - case KEY_SHIFT_R: - case KEY_CONTROL_L: - case KEY_CONTROL_R: - case KEY_ALT_L: - case KEY_ALT_R: - break; + case KEY_SHIFT_L: + case KEY_SHIFT_R: + case KEY_CONTROL_L: + case KEY_CONTROL_R: + case KEY_ALT_L: + case KEY_ALT_R: + break; #endif - default: - switch (escapes) { - case 0: - switch (kbinput) { - case NANO_CONTROL_8: - retval = ISSET(REBIND_DELETE) ? - NANO_DELETE_KEY : NANO_BACKSPACE_KEY; - break; - case KEY_DOWN: - retval = NANO_NEXTLINE_KEY; - break; - case KEY_UP: - retval = NANO_PREVLINE_KEY; - break; - case KEY_LEFT: - retval = NANO_BACK_KEY; - break; - case KEY_RIGHT: - retval = NANO_FORWARD_KEY; - break; + default: + switch (escapes) { + case 0: + switch (kbinput->key) { + case NANO_CONTROL_8: + retval = ISSET(REBIND_DELETE) ? + NANO_DELETE_KEY : + NANO_BACKSPACE_KEY; + break; + case KEY_DOWN: + retval = NANO_NEXTLINE_KEY; + break; + case KEY_UP: + retval = NANO_PREVLINE_KEY; + break; + case KEY_LEFT: + retval = NANO_BACK_KEY; + break; + case KEY_RIGHT: + retval = NANO_FORWARD_KEY; + break; #ifdef KEY_HOME - /* HP-UX 10 and 11 don't support KEY_HOME. */ - case KEY_HOME: - retval = NANO_HOME_KEY; - break; -#endif - case KEY_BACKSPACE: - retval = NANO_BACKSPACE_KEY; - break; - case KEY_DC: - retval = ISSET(REBIND_DELETE) ? - NANO_BACKSPACE_KEY : NANO_DELETE_KEY; - break; - case KEY_IC: - retval = NANO_INSERTFILE_KEY; - break; - case KEY_NPAGE: - retval = NANO_NEXTPAGE_KEY; - break; - case KEY_PPAGE: - retval = NANO_PREVPAGE_KEY; - break; - case KEY_ENTER: - retval = NANO_ENTER_KEY; - break; - case KEY_A1: /* Home (7) on numeric keypad - * with NumLock off. */ - retval = NANO_HOME_KEY; - break; - case KEY_A3: /* PageUp (9) on numeric keypad - * with NumLock off. */ - retval = NANO_PREVPAGE_KEY; - break; - case KEY_B2: /* Center (5) on numeric keypad - * with NumLock off. */ - break; - case KEY_C1: /* End (1) on numeric keypad - * with NumLock off. */ - retval = NANO_END_KEY; - break; - case KEY_C3: /* PageDown (4) on numeric - * keypad with NumLock off. */ - retval = NANO_NEXTPAGE_KEY; - break; + /* HP-UX 10 and 11 don't support + * KEY_HOME. */ + case KEY_HOME: + retval = NANO_HOME_KEY; + break; +#endif + case KEY_BACKSPACE: + retval = NANO_BACKSPACE_KEY; + break; + case KEY_DC: + retval = ISSET(REBIND_DELETE) ? + NANO_BACKSPACE_KEY : + NANO_DELETE_KEY; + break; + case KEY_IC: + retval = NANO_INSERTFILE_KEY; + break; + case KEY_NPAGE: + retval = NANO_NEXTPAGE_KEY; + break; + case KEY_PPAGE: + retval = NANO_PREVPAGE_KEY; + break; + case KEY_ENTER: + retval = NANO_ENTER_KEY; + break; + case KEY_A1: /* Home (7) on numeric + * keypad with NumLock + * off. */ + retval = NANO_HOME_KEY; + break; + case KEY_A3: /* PageUp (9) on numeric + * keypad with NumLock + * off. */ + retval = NANO_PREVPAGE_KEY; + break; + case KEY_B2: /* Center (5) on numeric + * keypad with NumLock + * off. */ + break; + case KEY_C1: /* End (1) on numeric + * keypad with NumLock + * off. */ + retval = NANO_END_KEY; + break; + case KEY_C3: /* PageDown (4) on + * numeric keypad with + * NumLock off. */ + retval = NANO_NEXTPAGE_KEY; + break; #ifdef KEY_BEG - /* Slang doesn't support KEY_BEG. */ - case KEY_BEG: /* Center (5) on numeric keypad - * with NumLock off. */ - break; + /* Slang doesn't support KEY_BEG. */ + case KEY_BEG: /* Center (5) on numeric + * keypad with NumLock + * off. */ + break; #endif #ifdef KEY_END - /* HP-UX 10 and 11 don't support KEY_END. */ - case KEY_END: - retval = NANO_END_KEY; - break; + /* HP-UX 10 and 11 don't support KEY_END. */ + case KEY_END: + retval = NANO_END_KEY; + break; #endif #ifdef KEY_SUSPEND - /* Slang doesn't support KEY_SUSPEND. */ - case KEY_SUSPEND: - retval = NANO_SUSPEND_KEY; - break; + /* Slang doesn't support KEY_SUSPEND. */ + case KEY_SUSPEND: + retval = NANO_SUSPEND_KEY; + break; #endif #ifdef KEY_SLEFT - /* Slang doesn't support KEY_SLEFT. */ - case KEY_SLEFT: - retval = NANO_BACK_KEY; - break; + /* Slang doesn't support KEY_SLEFT. */ + case KEY_SLEFT: + retval = NANO_BACK_KEY; + break; #endif #ifdef KEY_SRIGHT - /* Slang doesn't support KEY_SRIGHT. */ - case KEY_SRIGHT: - retval = NANO_FORWARD_KEY; - break; -#endif - default: - retval = kbinput; - break; - } - break; - case 1: - /* One escape followed by a non-escape: escape - * sequence mode. Reset the escape counter and set - * escape_seq to TRUE. */ - escapes = 0; - *escape_seq = TRUE; - break; - case 2: - if (kbinput >= '0' && kbinput <= '9') { + /* Slang doesn't support KEY_SRIGHT. */ + case KEY_SRIGHT: + retval = NANO_FORWARD_KEY; + break; +#endif + default: + retval = kbinput->key; + break; + } + break; + case 1: + /* One escape followed by a non-escape: escape + * sequence mode. Reset the escape counter. If + * there aren't any other keys waiting, we have + * a meta key sequence, so set meta_key to TRUE + * and save the lowercase version of the + * non-escape character as the result. If there + * are other keys waiting, we have a true escape + * sequence, so interpret it. */ + escapes = 0; + if (get_buffer_len() == 0) { + *meta_key = TRUE; + retval = tolower(kbinput->key); + } else { + buffer *escape_kbinput; + int *sequence; + size_t seq_len; + bool ignore_seq; + + /* Put back the non-escape character, get + * the complete escape sequence, translate + * its key values into the corresponding key + * value, and save that as the result. */ + unget_input(kbinput, 1); + seq_len = get_buffer_len(); + escape_kbinput = get_input(NULL, seq_len); + sequence = buffer_to_keys(escape_kbinput, + seq_len); + retval = get_escape_seq_kbinput(sequence, + seq_len, &ignore_seq); + + /* If the escape sequence is unrecognized + * and not ignored, put back all of its + * characters except for the initial + * escape. */ + if (retval == ERR && !ignore_seq) + unget_input(escape_kbinput, seq_len); + + free(escape_kbinput); + } + break; + case 2: /* Two escapes followed by one or more decimal - * digits: ASCII character sequence mode. If - * the digit sequence's range is limited to 2XX - * (the first digit is in the '0' to '2' range - * and it's the first digit, or if it's in the - * full digit range and it's not the first - * digit), increment the ASCII digit counter and - * interpret the digit. If the digit sequence's - * range is not limited to 2XX, fall through. */ - if (kbinput <= '2' || ascii_digits > 0) { - ascii_digits++; - kbinput = get_ascii_kbinput(kbinput, - ascii_digits + * digits: word sequence mode. If the word + * sequence's range is limited to 6XXXX (the + * first digit is in the '0' to '6' range and + * it's the first digit, or it's in the '0' to + * '9' range and it's not the first digit), + * increment the word sequence counter, + * interpret the digit, and save the + * corresponding word value as the result. If + * the word sequence's range is not limited to + * 6XXXX, fall through. */ + if (('0' <= kbinput->key && kbinput->key <= '6' + && word_digits == 0) || + ('0' <= kbinput->key && kbinput->key <= '9' + && word_digits > 0)) { + word_digits++; + retval = get_word_kbinput(kbinput->key #ifndef NANO_SMALL , FALSE #endif ); - if (kbinput != ERR) { - /* If we've read in a complete ASCII - * digit sequence, reset the ASCII digit - * counter and the escape counter and - * save the corresponding ASCII - * character as the result. */ - ascii_digits = 0; + if (retval != ERR) { + /* If we've read in a complete word + * sequence, reset the word sequence + * counter and the escape counter, and + * mark it so that it isn't interpreted + * as an extended keypad value. */ + word_digits = 0; escapes = 0; - retval = kbinput; + no_func_key = TRUE; + } + } else { + /* Reset the escape counter. */ + escapes = 0; + if (word_digits == 0) + /* Two escapes followed by a non-decimal + * digit or a decimal digit that would + * create a word sequence greater than + * 6XXXX, and we're not in the middle of + * a word sequence: control character + * sequence mode. Interpret the control + * sequence and save the corresponding + * control character as the result. */ + retval = get_control_kbinput(kbinput->key); + else { + /* If we're in the middle of a word + * sequence, reset the word sequence + * counter and save the character we got + * as the result. */ + word_digits = 0; + retval = kbinput->key; } } - } else { - /* Reset the escape counter. */ - escapes = 0; - if (ascii_digits == 0) - /* Two escapes followed by a non-decimal - * digit or a decimal digit that would - * create an ASCII digit sequence greater - * than 2XX, and we're not in the middle of - * an ASCII character sequence: control - * character sequence mode. Interpret the - * control sequence and save the - * corresponding control character as the - * result. */ - retval = get_control_kbinput(kbinput); - else { - /* If we were in the middle of an ASCII - * character sequence, reset the ASCII digit - * counter and save the character we got as - * the result. */ - ascii_digits = 0; - retval = kbinput; - } - } - break; - } - } - -#ifdef DEBUG - fprintf(stderr, "get_translated_kbinput(): kbinput = %d, escape_seq = %d, escapes = %d, ascii_digits = %d, retval = %d\n", kbinput, (int)*escape_seq, escapes, ascii_digits, retval); -#endif - - /* Return the result. */ - return retval; -} - -/* Translate an ASCII character sequence: turn a three-digit decimal - * ASCII code from 000-255 into its corresponding ASCII character. */ -int get_ascii_kbinput(int kbinput, int ascii_digits -#ifndef NANO_SMALL - , bool reset -#endif - ) -{ - static int ascii_kbinput = 0; - int retval = ERR; - -#ifndef NANO_SMALL - if (reset) { - ascii_kbinput = 0; - return ERR; - } -#endif - - switch (ascii_digits) { - case 1: - /* Read in the first of the three ASCII digits. */ - - /* Add the digit we got to the 100's position of the ASCII - * character sequence holder. */ - if ('0' <= kbinput && kbinput <= '2') - ascii_kbinput += (kbinput - '0') * 100; - else - retval = kbinput; - break; - case 2: - /* Read in the second of the three ASCII digits. */ - - /* Add the digit we got to the 10's position of the ASCII - * character sequence holder. */ - if (('0' <= kbinput && kbinput <= '5') || - (ascii_kbinput < 200 && '6' <= kbinput && - kbinput <= '9')) - ascii_kbinput += (kbinput - '0') * 10; - else - retval = kbinput; - break; - case 3: - /* Read in the third of the three ASCII digits. */ - - /* Add the digit we got to the 1's position of the ASCII - * character sequence holder, and save the corresponding - * ASCII character as the result. */ - if (('0' <= kbinput && kbinput <= '5') || - (ascii_kbinput < 250 && '6' <= kbinput && - kbinput <= '9')) { - ascii_kbinput += (kbinput - '0'); - retval = ascii_kbinput; - } else - retval = kbinput; - break; - default: - retval = kbinput; - break; + break; + } + } } -#ifdef DEBUG - fprintf(stderr, "get_ascii_kbinput(): kbinput = %d, ascii_digits = %d, ascii_kbinput = %d, retval = %d\n", kbinput, ascii_digits, ascii_kbinput, retval); -#endif - - /* If the result is an ASCII character, reset the ASCII character - * sequence holder. */ + /* If we have a result and it's an extended keypad value, set + * func_key to TRUE. */ if (retval != ERR) - ascii_kbinput = 0; - - return retval; -} - -/* Translate a control character sequence: turn an ASCII non-control - * character into its corresponding control character. */ -int get_control_kbinput(int kbinput) -{ - int retval = ERR; - - /* We don't handle Ctrl-2 here, since Esc Esc 2 could be the first - * part of an ASCII character sequence. */ - - /* Ctrl-2 (Ctrl-Space) == Ctrl-@ == Ctrl-` */ - if (kbinput == ' ' || kbinput == '@' || kbinput == '`') - retval = NANO_CONTROL_SPACE; - /* Ctrl-3 (Ctrl-[, Esc) to Ctrl-7 (Ctrl-_) */ - else if (kbinput >= '3' && kbinput <= '7') - retval = kbinput - 24; - /* Ctrl-8 (Ctrl-?) */ - else if (kbinput == '8' || kbinput == '?') - retval = NANO_CONTROL_8; - /* Ctrl-A to Ctrl-_ */ - else if (kbinput >= 'A' && kbinput <= '_') - retval = kbinput - 64; - /* Ctrl-a to Ctrl-~ */ - else if (kbinput >= 'a' && kbinput <= '~') - retval = kbinput - 96; - else - retval = kbinput; + *func_key = !is_byte_char(retval); #ifdef DEBUG - fprintf(stderr, "get_control_kbinput(): kbinput = %d, retval = %d\n", kbinput, retval); + fprintf(stderr, "parse_kbinput(): kbinput->key = %d, meta_key = %d, func_key = %d, escapes = %d, word_digits = %d, retval = %d\n", kbinput->key, (int)*meta_key, (int)*func_key, escapes, word_digits, retval); #endif + /* Return the result. */ return retval; } @@ -555,20 +681,20 @@ int get_control_kbinput(int kbinput) * ERR and set ignore_seq to TRUE; if it's unrecognized, return ERR and * set ignore_seq to FALSE. Assume that Escape has already been read * in. */ -int get_escape_seq_kbinput(const int *escape_seq, size_t es_len, bool +int get_escape_seq_kbinput(const int *sequence, size_t seq_len, bool *ignore_seq) { int retval = ERR; *ignore_seq = FALSE; - if (es_len > 1) { - switch (escape_seq[0]) { + if (seq_len > 1) { + switch (sequence[0]) { case 'O': - switch (escape_seq[1]) { + switch (sequence[1]) { case '2': - if (es_len >= 3) { - switch (escape_seq[2]) { + if (seq_len >= 3) { + switch (sequence[2]) { case 'P': /* Esc O 2 P == F13 on * xterm. */ retval = KEY_F(13); @@ -595,7 +721,7 @@ int get_escape_seq_kbinput(const int *escape_seq, size_t es_len, bool * VT100/VT320/xterm. */ case 'D': /* Esc O D == Left on * VT100/VT320/xterm. */ - retval = get_escape_seq_abcd(escape_seq[1]); + retval = get_escape_seq_abcd(sequence[1]); break; case 'E': /* Esc O E == Center (5) on numeric keypad * with NumLock off on xterm. */ @@ -650,7 +776,7 @@ int get_escape_seq_kbinput(const int *escape_seq, size_t es_len, bool case 'b': /* Esc O b == Ctrl-Down on rxvt. */ case 'c': /* Esc O c == Ctrl-Right on rxvt. */ case 'd': /* Esc O d == Ctrl-Left on rxvt. */ - retval = get_escape_seq_abcd(escape_seq[1]); + retval = get_escape_seq_abcd(sequence[1]); break; case 'j': /* Esc O j == '*' on numeric keypad with * NumLock off on VT100/VT220/VT320/xterm/ @@ -735,20 +861,20 @@ int get_escape_seq_kbinput(const int *escape_seq, size_t es_len, bool } break; case 'o': - switch (escape_seq[1]) { + switch (sequence[1]) { case 'a': /* Esc o a == Ctrl-Up on Eterm. */ case 'b': /* Esc o b == Ctrl-Down on Eterm. */ case 'c': /* Esc o c == Ctrl-Right on Eterm. */ case 'd': /* Esc o d == Ctrl-Left on Eterm. */ - retval = get_escape_seq_abcd(escape_seq[1]); + retval = get_escape_seq_abcd(sequence[1]); break; } break; case '[': - switch (escape_seq[1]) { + switch (sequence[1]) { case '1': - if (es_len >= 3) { - switch (escape_seq[2]) { + if (seq_len >= 3) { + switch (sequence[2]) { case '1': /* Esc [ 1 1 ~ == F1 on rxvt/ * Eterm. */ retval = KEY_F(1); @@ -785,11 +911,11 @@ int get_escape_seq_kbinput(const int *escape_seq, size_t es_len, bool retval = KEY_F(8); break; case ';': - if (es_len >= 4) { - switch (escape_seq[3]) { + if (seq_len >= 4) { + switch (sequence[3]) { case '2': - if (es_len >= 5) { - switch (escape_seq[4]) { + if (seq_len >= 5) { + switch (sequence[4]) { case 'A': /* Esc [ 1 ; 2 A == Shift-Up on * xterm. */ case 'B': /* Esc [ 1 ; 2 B == Shift-Down on @@ -798,14 +924,14 @@ int get_escape_seq_kbinput(const int *escape_seq, size_t es_len, bool * xterm. */ case 'D': /* Esc [ 1 ; 2 D == Shift-Left on * xterm. */ - retval = get_escape_seq_abcd(escape_seq[4]); + retval = get_escape_seq_abcd(sequence[4]); break; } } break; case '5': - if (es_len >= 5) { - switch (escape_seq[4]) { + if (seq_len >= 5) { + switch (sequence[4]) { case 'A': /* Esc [ 1 ; 5 A == Ctrl-Up on * xterm. */ case 'B': /* Esc [ 1 ; 5 B == Ctrl-Down on @@ -814,7 +940,7 @@ int get_escape_seq_kbinput(const int *escape_seq, size_t es_len, bool * xterm. */ case 'D': /* Esc [ 1 ; 5 D == Ctrl-Left on * xterm. */ - retval = get_escape_seq_abcd(escape_seq[4]); + retval = get_escape_seq_abcd(sequence[4]); break; } } @@ -830,8 +956,8 @@ int get_escape_seq_kbinput(const int *escape_seq, size_t es_len, bool } break; case '2': - if (es_len >= 3) { - switch (escape_seq[2]) { + if (seq_len >= 3) { + switch (sequence[2]) { case '0': /* Esc [ 2 0 ~ == F9 on * VT220/VT320/Linux console/ * xterm/rxvt/Eterm. */ @@ -922,7 +1048,7 @@ int get_escape_seq_kbinput(const int *escape_seq, size_t es_len, bool case 'D': /* Esc [ D == Left on ANSI/VT220/Linux * console/FreeBSD console/Mach console/ * rxvt/Eterm. */ - retval = get_escape_seq_abcd(escape_seq[1]); + retval = get_escape_seq_abcd(sequence[1]); break; case 'E': /* Esc [ E == Center (5) on numeric keypad * with NumLock off on FreeBSD console. */ @@ -955,8 +1081,8 @@ int get_escape_seq_kbinput(const int *escape_seq, size_t es_len, bool retval = KEY_F(2); break; case 'O': - if (es_len >= 3) { - switch (escape_seq[2]) { + if (seq_len >= 3) { + switch (sequence[2]) { case 'P': /* Esc [ O P == F1 on * xterm. */ retval = KEY_F(1); @@ -1017,11 +1143,11 @@ int get_escape_seq_kbinput(const int *escape_seq, size_t es_len, bool case 'c': /* Esc [ c == Shift-Right on rxvt/ * Eterm. */ case 'd': /* Esc [ d == Shift-Left on rxvt/Eterm. */ - retval = get_escape_seq_abcd(escape_seq[1]); + retval = get_escape_seq_abcd(sequence[1]); break; case '[': - if (es_len >= 3) { - switch (escape_seq[2]) { + if (seq_len >= 3) { + switch (sequence[2]) { case 'A': /* Esc [ [ A == F1 on Linux * console. */ retval = KEY_F(1); @@ -1076,103 +1202,165 @@ int get_escape_seq_abcd(int kbinput) } } -/* Read in a string of input characters (e.g. an escape sequence) - * verbatim. Return the length of the string in v_len. Assume - * nodelay(win) is FALSE. */ -int *get_verbatim_kbinput(WINDOW *win, size_t *v_len, bool allow_ascii) +/* Translate a word sequence: turn a three-digit decimal number from + * 000 to 255 into its corresponding word value. */ +int get_word_kbinput(int kbinput +#ifndef NANO_SMALL + , bool reset +#endif + ) { - int kbinput, *v_kbinput; - size_t i = 0, v_newlen = 0; + static int word_digits = 0; + static int word_kbinput = 0; + int retval = ERR; #ifndef NANO_SMALL - allow_pending_sigwinch(TRUE); + if (reset) { + word_digits = 0; + word_kbinput = 0; + return ERR; + } #endif - *v_len = 0; - v_kbinput = (int *)nmalloc(sizeof(int)); + /* Increment the word digit counter. */ + word_digits++; - /* Turn off flow control characters if necessary so that we can type - * them in verbatim, and turn the keypad off so that we don't get - * extended keypad values outside the ASCII range. */ - if (ISSET(PRESERVE)) - disable_flow_control(); - keypad(win, FALSE); + switch (word_digits) { + case 1: + /* One digit: reset the word sequence holder and add the + * digit we got to the 10000's position of the word sequence + * holder. */ + word_kbinput = 0; + if ('0' <= kbinput && kbinput <= '6') + word_kbinput += (kbinput - '0') * 10000; + else + /* If the character we got isn't a decimal digit, or if + * it is and it would put the word sequence out of word + * range, save it as the result. */ + retval = kbinput; + break; + case 2: + /* Two digits: add the digit we got to the 1000's position + * of the word sequence holder. */ + if (('0' <= kbinput && kbinput <= '5') || + (word_kbinput < 60000 && '6' <= kbinput && + kbinput <= '9')) + word_kbinput += (kbinput - '0') * 1000; + else + /* If the character we got isn't a decimal digit, or if + * it is and it would put the word sequence out of word + * range, save it as the result. */ + retval = kbinput; + break; + case 3: + /* Three digits: add the digit we got to the 100's position + * of the word sequence holder. */ + if (('0' <= kbinput && kbinput <= '5') || + (word_kbinput < 65000 && '6' <= kbinput && + kbinput <= '9')) + word_kbinput += (kbinput - '0') * 100; + else + /* If the character we got isn't a decimal digit, or if + * it is and it would put the word sequence out of word + * range, save it as the result. */ + retval = kbinput; + break; + case 4: + /* Four digits: add the digit we got to the 10's position of + * the word sequence holder. */ + if (('0' <= kbinput && kbinput <= '3') || + (word_kbinput < 65500 && '4' <= kbinput && + kbinput <= '9')) + word_kbinput += (kbinput - '0') * 10; + else + /* If the character we got isn't a decimal digit, or if + * it is and it would put the word sequence out of word + * range, save it as the result. */ + retval = kbinput; + break; + case 5: + /* Five digits: add the digit we got to the 1's position of + * the word sequence holder, and save the corresponding word + * value as the result. */ + if (('0' <= kbinput && kbinput <= '5') || + (word_kbinput < 65530 && '6' <= kbinput && + kbinput <= '9')) { + word_kbinput += (kbinput - '0'); + retval = word_kbinput; + } else + /* If the character we got isn't a decimal digit, or if + * it is and it would put the word sequence out of word + * range, save it as the result. */ + retval = kbinput; + break; + default: + /* More than three digits: save the character we got as the + * result. */ + retval = kbinput; + break; + } - /* If first is ERR, read the first character using blocking input, - * since using non-blocking input will eat up all unused CPU. Then - * increment v_len and save the character in v_kbinput. */ - kbinput = wgetch(win); - (*v_len)++; - v_kbinput[0] = kbinput; + /* If we have a result, reset the word digit counter and the word + * sequence holder. */ + if (retval != ERR) { + word_digits = 0; + word_kbinput = 0; + } #ifdef DEBUG - fprintf(stderr, "get_verbatim_kbinput(): kbinput = %d, v_len = %lu\n", kbinput, (unsigned long)*v_len); + fprintf(stderr, "get_word_kbinput(): kbinput = %d, word_digits = %d, word_kbinput = %d, retval = %d\n", kbinput, word_digits, word_kbinput, retval); #endif - /* Read any following characters using non-blocking input, until - * there aren't any left to be read, and save the complete string of - * characters in v_kbinput, incrementing v_len accordingly. We read - * them all at once in order to minimize the chance that there might - * be a delay greater than nodelay() provides for between them, in - * which case we'll stop before all of them are read. */ - nodelay(win, TRUE); - while ((kbinput = wgetch(win)) != ERR) { - (*v_len)++; - v_kbinput = (int *)nrealloc(v_kbinput, *v_len * sizeof(int)); - v_kbinput[*v_len - 1] = kbinput; + return retval; +} + +/* Translate a control character sequence: turn an ASCII non-control + * character into its corresponding control character. */ +int get_control_kbinput(int kbinput) +{ + int retval; + + /* Ctrl-2 (Ctrl-Space, Ctrl-@, Ctrl-`) */ + if (kbinput == '2' || kbinput == ' ' || kbinput == '@' || + kbinput == '`') + retval = NANO_CONTROL_SPACE; + /* Ctrl-3 (Ctrl-[, Esc) to Ctrl-7 (Ctrl-_) */ + else if ('3' <= kbinput && kbinput <= '7') + retval = kbinput - 24; + /* Ctrl-8 (Ctrl-?) */ + else if (kbinput == '8' || kbinput == '?') + retval = NANO_CONTROL_8; + /* Ctrl-A to Ctrl-_ */ + else if ('A' <= kbinput && kbinput <= '_') + retval = kbinput - 64; + /* Ctrl-a to Ctrl-~ */ + else if ('a' <= kbinput && kbinput <= '~') + retval = kbinput - 96; + else + retval = kbinput; + #ifdef DEBUG - fprintf(stderr, "get_verbatim_kbinput(): kbinput = %d, v_len = %lu\n", kbinput, (unsigned long)*v_len); + fprintf(stderr, "get_control_kbinput(): kbinput = %d, retval = %d\n", kbinput, retval); #endif - } - nodelay(win, FALSE); - /* Pass the string of characters to get_untranslated_kbinput(), one - * by one, so it can handle them as ASCII character sequences and/or - * escape sequences. Filter out ERR's from v_kbinput in the - * process; they shouldn't occur in the string of characters unless - * we're reading an incomplete sequence, in which case we only want - * to keep the complete sequence. */ - for (; i < *v_len; i++) { - v_kbinput[v_newlen] = get_untranslated_kbinput(v_kbinput[i], i, - allow_ascii -#ifndef NANO_SMALL - , FALSE -#endif - ); - if (v_kbinput[i] != ERR && v_kbinput[v_newlen] != ERR) - v_newlen++; - } + return retval; +} - if (v_newlen == 0) { - /* If there were no characters after the ERR's were filtered - * out, set v_len and reallocate v_kbinput to account for - * one character, and set that character to ERR. */ - *v_len = 1; - v_kbinput = (int *)nrealloc(v_kbinput, sizeof(int)); - v_kbinput[0] = ERR; - } else if (v_newlen != *v_len) { - /* If there were fewer characters after the ERR's were filtered - * out, set v_len and reallocate v_kbinput to account for - * the new number of characters. */ - *v_len = v_newlen; - v_kbinput = (int *)nrealloc(v_kbinput, *v_len * sizeof(int)); - } +/* Read in a string of characters verbatim, and return the length of the + * string in kbinput_len. Assume nodelay(win) is FALSE. */ +int *get_verbatim_kbinput(WINDOW *win, size_t *kbinput_len) +{ + int *retval; - /* If allow_ascii is TRUE and v_kbinput[0] is ERR, we need to - * complete an ASCII character sequence. Keep reading in characters - * using blocking input until we get a complete sequence. */ - if (allow_ascii && v_kbinput[0] == ERR) { - while (v_kbinput[0] == ERR) { - kbinput = wgetch(win); - v_kbinput[0] = get_untranslated_kbinput(kbinput, i, - allow_ascii -#ifndef NANO_SMALL - , FALSE -#endif - ); - i++; - } - } + /* Turn off flow control characters if necessary so that we can type + * them in verbatim, and turn the keypad off so that we don't get + * extended keypad values. */ + if (ISSET(PRESERVE)) + disable_flow_control(); + keypad(win, FALSE); + + /* Read in a stream of characters and interpret it if possible. */ + retval = parse_verbatim_kbinput(win, kbinput_len); /* Turn flow control characters back on if necessary and turn the * keypad back on now that we're done. */ @@ -1180,69 +1368,57 @@ int *get_verbatim_kbinput(WINDOW *win, size_t *v_len, bool allow_ascii) enable_flow_control(); keypad(win, TRUE); -#ifndef NANO_SMALL - allow_pending_sigwinch(FALSE); -#endif - - return v_kbinput; + return retval; } -int get_untranslated_kbinput(int kbinput, size_t position, bool - allow_ascii -#ifndef NANO_SMALL - , bool reset -#endif - ) +/* Read in a stream of all available characters. Translate the first + * few characters of the input into the corresponding word value if + * possible. After that, leave the input as-is. */ +int *parse_verbatim_kbinput(WINDOW *win, size_t *kbinput_len) { - static int ascii_digits = 0; - int retval; + buffer *kbinput, *sequence; + int word, *retval; + /* Read in the first keystroke. */ + while ((kbinput = get_input(win, 1)) == NULL); + + /* Check whether the first keystroke is a decimal digit. */ + word = get_word_kbinput(kbinput->key #ifndef NANO_SMALL - if (reset) { - ascii_digits = 0; - return ERR; - } + , FALSE #endif + ); + + /* If the first keystroke isn't a decimal digit, put back the first + * keystroke. */ + if (word != ERR) + unget_input(kbinput, 1); + /* Otherwise, read in keystrokes until we have a complete word + * sequence, and put back the corresponding word value. */ + else { + buffer word_kbinput; - if (allow_ascii) { - /* position is equal to the number of ASCII digits we've read so - * far, and kbinput is a digit from '0' to '9': ASCII character - * sequence mode. If the digit sequence's range is limited to - * 2XX (the first digit is in the '0' to '2' range and it's the - * first digit, or if it's in the full digit range and it's not - * the first digit), increment the ASCII digit counter and - * interpret the digit. If the digit sequence's range is not - * limited to 2XX, fall through. */ - if (position == ascii_digits && kbinput >= '0' && - kbinput <= '9') { - if (kbinput <= '2' || ascii_digits > 0) { - ascii_digits++; - kbinput = get_ascii_kbinput(kbinput, ascii_digits + while (word == ERR) { + while ((kbinput = get_input(win, 1)) == NULL); + word = get_word_kbinput(kbinput->key #ifndef NANO_SMALL - , FALSE + , FALSE #endif - ); - if (kbinput != ERR) - /* If we've read in a complete ASCII digit sequence, - * reset the ASCII digit counter. */ - ascii_digits = 0; - } - } else if (ascii_digits > 0) - /* position is not equal to the number of ASCII digits we've - * read or kbinput is a non-digit, and we're in the middle - * of an ASCII character sequence. Reset the ASCII digit - * counter. */ - ascii_digits = 0; - } + ); + } - /* Save the corresponding ASCII character as the result if we've - * read in a complete ASCII digit sequence, or the passed-in - * character if we haven't. */ - retval = kbinput; + word_kbinput.key = word; + word_kbinput.key_code = FALSE; -#ifdef DEBUG - fprintf(stderr, "get_untranslated_kbinput(): kbinput = %d, position = %lu, ascii_digits = %d\n", kbinput, (unsigned long)position, ascii_digits); -#endif + unget_input(&word_kbinput, 1); + } + + /* Get the complete sequence, and save the key values in it as the + * result. */ + *kbinput_len = get_buffer_len(); + sequence = get_input(NULL, *kbinput_len); + retval = buffer_to_keys(sequence, *kbinput_len); + free(sequence); return retval; } @@ -1325,9 +1501,9 @@ bool get_mouseinput(int *mouse_x, int *mouse_y, bool allow_shortcuts) * has, at the very least, an equivalent control key, an * equivalent primary meta key sequence, or both. */ if (s->ctrlval != NANO_NO_KEY) - unget_kbinput(s->ctrlval, FALSE); + unget_kbinput(s->ctrlval, FALSE, FALSE); else if (s->metaval != NANO_NO_KEY) - unget_kbinput(s->metaval, TRUE); + unget_kbinput(s->metaval, TRUE, FALSE); return TRUE; } @@ -1372,9 +1548,11 @@ const shortcut *get_shortcut(const shortcut *s_list, int *kbinput, bool if (slen > 0) { if (s->ctrlval != NANO_NO_KEY) { *meta_key = FALSE; + *func_key = FALSE; *kbinput = s->ctrlval; } else if (s->metaval != NANO_NO_KEY) { *meta_key = TRUE; + *func_key = FALSE; *kbinput = s->metaval; } return s; @@ -1404,149 +1582,6 @@ const toggle *get_toggle(int kbinput, bool meta_key) } #endif /* !NANO_SMALL */ -int get_edit_input(bool *meta_key, bool *func_key, bool allow_funcs) -{ - bool keyhandled = FALSE; - int kbinput, retval; - const shortcut *s; -#ifndef NANO_SMALL - const toggle *t; -#endif - - kbinput = get_kbinput(edit, meta_key, func_key); - - /* Universal shortcuts. These aren't in any shortcut lists, but we - * should handle them anyway. */ - switch (kbinput) { - case NANO_XON_KEY: - statusbar(_("XON ignored, mumble mumble.")); - return ERR; - case NANO_XOFF_KEY: - statusbar(_("XOFF ignored, mumble mumble.")); - return ERR; -#ifndef NANO_SMALL - case NANO_SUSPEND_KEY: - if (ISSET(SUSPEND)) - do_suspend(0); - return ERR; -#endif -#ifndef DISABLE_MOUSE - case KEY_MOUSE: - if (get_edit_mouse()) { - kbinput = get_kbinput(edit, meta_key, func_key); - break; - } else - return ERR; -#endif - } - - /* Check for a shortcut in the main list. */ - s = get_shortcut(main_list, &kbinput, meta_key, func_key); - - if (s != NULL) { - /* We got a shortcut. Run the shortcut's corresponding function - * if it has one. */ - if (s->func != do_cut_text) - cutbuffer_reset(); - if (s->func != NULL) { - if (allow_funcs) { - if (ISSET(VIEW_MODE) && !s->viewok) - print_view_warning(); - else - s->func(); - } - keyhandled = TRUE; - } - } - -#ifndef NANO_SMALL - else { - /* If we didn't get a shortcut, check for a toggle. */ - t = get_toggle(kbinput, *meta_key); - - /* We got a toggle. Switch the value of the toggle's - * corresponding flag. */ - if (t != NULL) { - cutbuffer_reset(); - if (allow_funcs) - do_toggle(t); - keyhandled = TRUE; - } - } -#endif - - /* If we got a shortcut with a corresponding function or a toggle, - * reset meta_key and retval. If we didn't, keep the value of - * meta_key and return the key we got in retval. */ - if (allow_funcs && keyhandled) { - *meta_key = FALSE; - retval = ERR; - } else { - cutbuffer_reset(); - retval = kbinput; - } - - return retval; -} - -#ifndef DISABLE_MOUSE -bool get_edit_mouse(void) -{ - int mouse_x, mouse_y; - bool retval; - - retval = get_mouseinput(&mouse_x, &mouse_y, TRUE); - - if (!retval) { - /* We can click in the edit window to move the cursor. */ - if (wenclose(edit, mouse_y, mouse_x)) { - bool sameline; - /* Did they click on the line with the cursor? If they - * clicked on the cursor, we set the mark. */ - size_t xcur; - /* The character they clicked on. */ - - /* Subtract out the size of topwin. Perhaps we need a - * constant somewhere? */ - mouse_y -= 2; - - sameline = (mouse_y == current_y); - - /* Move to where the click occurred. */ - for (; current_y < mouse_y && current->next != NULL; current_y++) - current = current->next; - for (; current_y > mouse_y && current->prev != NULL; current_y--) - current = current->prev; - - xcur = actual_x(current->data, get_page_start(xplustabs()) + - mouse_x); - -#ifndef NANO_SMALL - /* Clicking where the cursor is toggles the mark, as does - * clicking beyond the line length with the cursor at the - * end of the line. */ - if (sameline && xcur == current_x) { - if (ISSET(VIEW_MODE)) { - print_view_warning(); - return retval; - } - do_mark(); - } -#endif - - current_x = xcur; - placewewant = xplustabs(); - edit_refresh(); - } - } - /* FIXME: If we clicked on a location in the statusbar, the cursor - * should move to the location we clicked on. This functionality - * should be in get_statusbar_mouse() when it's written. */ - - return retval; -} -#endif /* !DISABLE_MOUSE */ - /* Return the placewewant associated with current_x. That is, xplustabs * is the zero-based column position of the cursor. Value is no smaller * than current_x. */ @@ -3141,14 +3176,14 @@ int do_yesno(bool all, const char *msg) if (kbinput == NANO_CANCEL_KEY) ok = -1; #ifndef DISABLE_MOUSE - /* Look ma! We get to duplicate lots of code from - * get_edit_mouse()!! */ + /* Look, ma! We get to duplicate lots of code from + * do_mouse()!! */ else if (kbinput == KEY_MOUSE) { get_mouseinput(&mouse_x, &mouse_y, FALSE); if (mouse_x != -1 && mouse_y != -1 && !ISSET(NO_HELP) && - wenclose(bottomwin, mouse_y, mouse_x) && mouse_x < - (width * 2) && mouse_y >= editwinrows + 3) { + wenclose(bottomwin, mouse_y, mouse_x) && + mouse_x < (width * 2) && mouse_y >= editwinrows + 3) { int x = mouse_x / width; /* Did we click in the first column of shortcuts, or * the second? */ @@ -3191,8 +3226,9 @@ void total_refresh(void) clearok(topwin, FALSE); clearok(edit, FALSE); clearok(bottomwin, FALSE); - edit_refresh(); titlebar(NULL); + edit_refresh(); + /* FIXME: bottomwin needs to be refreshed too. */ } void display_main_list(void) -- 2.39.5