]> git.wh0rd.org Git - nano.git/commitdiff
massive overhaul to support buffered input and output, the first steps
authorDavid Lawrence Ramsey <pooka109@gmail.com>
Sat, 4 Dec 2004 17:41:52 +0000 (17:41 +0000)
committerDavid Lawrence Ramsey <pooka109@gmail.com>
Sat, 4 Dec 2004 17:41:52 +0000 (17:41 +0000)
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
src/files.c
src/global.c
src/nano.c
src/nano.h
src/proto.h
src/rcfile.c
src/utils.c
src/winio.c

index e95baa55807734920a592ec2a6f6774c9a6fdcd7..412cb1c522d0565aaa75f0c12ceae4c75a1732bd 100644 (file)
--- 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
index e725dc2967414852e2b3795da7648c73acae21ba..db2809fcbc7fcf31d1fca22e0463a17e78d11e83 100644 (file)
@@ -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;
index dd9c2ad4b8a141addbae8ef6db41d17a5915266a..4c4d2ec2ecf25f6fd4d261a5f06ed9b5f32f78fa 100644 (file)
@@ -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
index 9cdfc71132f61ba461f6750d8ab2004362ebdab4..64c0d8e37ccc8c7edc02604c8a83cdf90132bb72 100644 (file)
@@ -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(&current->data[current_x + 1], &current->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(&current->data[current_x + key_len],
+               &current->data[current_x],
+               current_len - current_x + key_len);
+       charcpy(&current->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);
 }
index c8e0d12aacc7c1016ac12703a6c2450b51253743..9a217f264a673c84b2bb7716047e2b9defee5edb 100644 (file)
@@ -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)
 
 #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
index 18ea60dd8cd002de8bd5f2498a8509c2288698a2..e47099154fc4a5b3c24bc5c2e04cf0f85166260f 100644 (file)
@@ -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);
index 8f1780cb400dec0c21f2713c569f15aa31ff538f..a02805e84325f7dccfab5b0e429d7e56b94909bb 100644 (file)
@@ -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
index da494e0f6c019a6c5e3fc856a8baad486791e928..bda6ce74146421d03703a6fe7fc2faf3274e2e0a 100644 (file)
@@ -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;
index 60866bb7619be8fff90e4f5237ce6af4badb8583..c3b810bc73f21a25b1d95608c232831547fb677a 100644 (file)
 #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)