OSDN Git Service

Upgrade to mksh R56b.
[android-x86/external-mksh.git] / src / edit.c
index 58eaf7f..8bccf13 100644 (file)
 
 #ifndef MKSH_NO_CMDLINE_EDITING
 
-__RCSID("$MirOS: src/bin/mksh/edit.c,v 1.321 2017/04/12 16:46:20 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/edit.c,v 1.340 2017/08/27 23:33:50 tg Exp $");
 
 /*
  * in later versions we might use libtermcap for this, but since external
  * dependencies are problematic, this has not yet been decided on; another
- * good string is "\033c" except on hardware terminals like the DEC VT420
- * which do a full power cycle then...
+ * good string is KSH_ESC_STRING "c" except on hardware terminals like the
+ * DEC VT420 which do a full power cycle then...
  */
 #ifndef MKSH_CLS_STRING
-#define MKSH_CLS_STRING                "\033[;H\033[J"
+#define MKSH_CLS_STRING                KSH_ESC_STRING "[;H" KSH_ESC_STRING "[J"
 #endif
 
 /* tty driver characters we are interested in */
@@ -76,7 +76,7 @@ static int modified;                  /* buffer has been "modified" */
 static char *holdbufp;                 /* place to hold last edit buffer */
 
 /* 0=dumb 1=tmux (for now) */
-static bool x_term_mode;
+static uint8_t x_term_mode;
 
 static void x_adjust(void);
 static int x_getc(void);
@@ -97,6 +97,7 @@ static void x_init_prompt(bool);
 #if !MKSH_S_NOVI
 static int x_vi(char *);
 #endif
+static void x_intr(int, int) MKSH_A_NORETURN;
 
 #define x_flush()      shf_flush(shl_out)
 #if defined(MKSH_SMALL) && !defined(MKSH_SMALL_BUT_FAST)
@@ -309,14 +310,14 @@ x_glob_hlp_add_qchar(char *cp)
                         * empirically made list of chars to escape
                         * for globbing as well as QCHAR itself
                         */
-                       switch (ch) {
+                       switch (ord(ch)) {
                        case QCHAR:
-                       case '$':
-                       case '*':
-                       case '?':
-                       case '[':
-                       case '\\':
-                       case '`':
+                       case ord('$'):
+                       case ord('*'):
+                       case ord('?'):
+                       case ord('['):
+                       case ord('\\'):
+                       case ord('`'):
                                *dp++ = QCHAR;
                                break;
                        }
@@ -467,7 +468,7 @@ path_order_cmp(const void *aa, const void *bb)
        const struct path_order_info *b = (const struct path_order_info *)bb;
        int t;
 
-       if ((t = strcmp(a->word + a->base, b->word + b->base)))
+       if ((t = ascstrcmp(a->word + a->base, b->word + b->base)))
                return (t);
        if (a->path_order > b->path_order)
                return (1);
@@ -535,7 +536,7 @@ x_command_glob(int flags, char *toglob, char ***wordsp)
                char **words = (char **)XPptrv(w);
                size_t i, j;
 
-               qsort(words, nwords, sizeof(void *), xstrcmp);
+               qsort(words, nwords, sizeof(void *), ascpstrcmp);
                for (i = j = 0; i < nwords - 1; i++) {
                        if (strcmp(words[i], words[i + 1]))
                                words[j++] = words[i];
@@ -552,8 +553,7 @@ x_command_glob(int flags, char *toglob, char ***wordsp)
        return (nwords);
 }
 
-#define IS_WORDC(c)    (!ctype(c, C_LEX1) && (c) != '\'' && (c) != '"' && \
-                           (c) != '`' && (c) != '=' && (c) != ':')
+#define IS_WORDC(c)    (!ctype(c, C_EDNWC))
 
 static int
 x_locate_word(const char *buf, int buflen, int pos, int *startp,
@@ -588,9 +588,9 @@ x_locate_word(const char *buf, int buflen, int pos, int *startp,
                int p = start - 1;
 
                /* Figure out if this is a command */
-               while (p >= 0 && ksh_isspace(buf[p]))
+               while (p >= 0 && ctype(buf[p], C_SPACE))
                        p--;
-               iscmd = p < 0 || vstrchr(";|&()`", buf[p]);
+               iscmd = p < 0 || ctype(buf[p], C_EDCMD);
                if (iscmd) {
                        /*
                         * If command has a /, path, etc. is not searched;
@@ -649,11 +649,12 @@ x_cf_glob(int *flagsp, const char *buf, int buflen, int pos, int *startp,
                for (s = toglob; *s; s++) {
                        if (*s == '\\' && s[1])
                                s++;
-                       else if (*s == '?' || *s == '*' || *s == '[' ||
-                           *s == '$' ||
+                       else if (ctype(*s, C_QUEST | C_DOLAR) ||
+                           ord(*s) == ord('*') || ord(*s) == ord('[') ||
                            /* ?() *() +() @() !() but two already checked */
-                           (s[1] == '(' /*)*/ &&
-                           (*s == '+' || *s == '@' || *s == '!'))) {
+                           (ord(s[1]) == ord('(' /*)*/) &&
+                           (ord(*s) == ord('+') || ord(*s) == ord('@') ||
+                           ord(*s) == ord('!')))) {
                                /*
                                 * just expand based on the extglob
                                 * or parameter
@@ -714,8 +715,8 @@ x_longest_prefix(int nwords, char * const * words)
                                break;
                        }
        /* false for nwords==1 as 0 = words[0][prefix_len] then */
-       if (UTFMODE && prefix_len && (words[0][prefix_len] & 0xC0) == 0x80)
-               while (prefix_len && (words[0][prefix_len] & 0xC0) != 0xC0)
+       if (UTFMODE && prefix_len && (rtt2asc(words[0][prefix_len]) & 0xC0) == 0x80)
+               while (prefix_len && (rtt2asc(words[0][prefix_len]) & 0xC0) != 0xC0)
                        --prefix_len;
        return (prefix_len);
 }
@@ -747,7 +748,7 @@ x_basename(const char *s, const char *se)
        const char *p;
 
        if (se == NULL)
-               se = s + strlen(s);
+               se = strnul(s);
        if (s == se)
                return (0);
 
@@ -799,7 +800,7 @@ glob_path(int flags, const char *pat, XPtrV *wp, const char *lpath)
        while (sp) {
                xp = Xstring(xs, xp);
                if (!(p = cstrchr(sp, MKSH_PATHSEPC)))
-                       p = sp + strlen(sp);
+                       p = strnul(sp);
                pathlen = p - sp;
                if (pathlen) {
                        /*
@@ -858,8 +859,7 @@ x_escape(const char *s, size_t len, int (*putbuf_func)(const char *, size_t))
        int rval = 0;
 
        while (wlen - add > 0)
-               if (vstrchr("\"#$&'()*:;<=>?[\\`{|}", s[add]) ||
-                   ctype(s[add], C_IFS)) {
+               if (ctype(s[add], C_IFS | C_EDQ)) {
                        if (putbuf_func(s, add) != 0) {
                                rval = -1;
                                break;
@@ -908,11 +908,6 @@ struct x_defbindings {
 #define        XF_NOBIND       2       /* not allowed to bind to function */
 #define        XF_PREFIX       4       /* function sets prefix */
 
-/* Separator for completion */
-#define        is_cfs(c)       ((c) == ' ' || (c) == '\t' || (c) == '"' || (c) == '\'')
-/* Separator for motion */
-#define        is_mfs(c)       (!(ksh_isalnux(c) || (c) == '$' || ((c) & 0x80)))
-
 #define X_NTABS                4                       /* normal, meta1, meta2, pc */
 #define X_TABSZ                256                     /* size of keydef tables etc */
 
@@ -991,6 +986,7 @@ static void x_bs3(char **);
 static int x_size2(char *, char **);
 static void x_zots(char *);
 static void x_zotc3(char **);
+static void x_vi_zotc(int);
 static void x_load_hist(char **);
 static int x_search(char *, int, int);
 #ifndef MKSH_SMALL
@@ -1036,56 +1032,56 @@ static const struct x_ftab x_ftab[] = {
 };
 
 static struct x_defbindings const x_defbindings[] = {
-       { XFUNC_del_back,               0, CTRL('?')    },
-       { XFUNC_del_bword,              1, CTRL('?')    },
-       { XFUNC_eot_del,                0, CTRL('D')    },
-       { XFUNC_del_back,               0, CTRL('H')    },
-       { XFUNC_del_bword,              1, CTRL('H')    },
+       { XFUNC_del_back,               0,  CTRL_QM     },
+       { XFUNC_del_bword,              1,  CTRL_QM     },
+       { XFUNC_eot_del,                0,  CTRL_D      },
+       { XFUNC_del_back,               0,  CTRL_H      },
+       { XFUNC_del_bword,              1,  CTRL_H      },
        { XFUNC_del_bword,              1,      'h'     },
        { XFUNC_mv_bword,               1,      'b'     },
        { XFUNC_mv_fword,               1,      'f'     },
        { XFUNC_del_fword,              1,      'd'     },
-       { XFUNC_mv_back,                0, CTRL('B')    },
-       { XFUNC_mv_forw,                0, CTRL('F')    },
-       { XFUNC_search_char_forw,       0, CTRL(']')    },
-       { XFUNC_search_char_back,       1, CTRL(']')    },
-       { XFUNC_newline,                0, CTRL('M')    },
-       { XFUNC_newline,                0, CTRL('J')    },
-       { XFUNC_end_of_text,            0, CTRL('_')    },
-       { XFUNC_abort,                  0, CTRL('G')    },
-       { XFUNC_prev_com,               0, CTRL('P')    },
-       { XFUNC_next_com,               0, CTRL('N')    },
-       { XFUNC_nl_next_com,            0, CTRL('O')    },
-       { XFUNC_search_hist,            0, CTRL('R')    },
+       { XFUNC_mv_back,                0,  CTRL_B      },
+       { XFUNC_mv_forw,                0,  CTRL_F      },
+       { XFUNC_search_char_forw,       0,  CTRL_BC     },
+       { XFUNC_search_char_back,       1,  CTRL_BC     },
+       { XFUNC_newline,                0,  CTRL_M      },
+       { XFUNC_newline,                0,  CTRL_J      },
+       { XFUNC_end_of_text,            0,  CTRL_US     },
+       { XFUNC_abort,                  0,  CTRL_G      },
+       { XFUNC_prev_com,               0,  CTRL_P      },
+       { XFUNC_next_com,               0,  CTRL_N      },
+       { XFUNC_nl_next_com,            0,  CTRL_O      },
+       { XFUNC_search_hist,            0,  CTRL_R      },
        { XFUNC_beg_hist,               1,      '<'     },
        { XFUNC_end_hist,               1,      '>'     },
        { XFUNC_goto_hist,              1,      'g'     },
-       { XFUNC_mv_end,                 0, CTRL('E')    },
-       { XFUNC_mv_beg,                 0, CTRL('A')    },
-       { XFUNC_draw_line,              0, CTRL('L')    },
-       { XFUNC_cls,                    1, CTRL('L')    },
-       { XFUNC_meta1,                  0, CTRL('[')    },
-       { XFUNC_meta2,                  0, CTRL('X')    },
-       { XFUNC_kill,                   0, CTRL('K')    },
-       { XFUNC_yank,                   0, CTRL('Y')    },
+       { XFUNC_mv_end,                 0,  CTRL_E      },
+       { XFUNC_mv_beg,                 0,  CTRL_A      },
+       { XFUNC_draw_line,              0,  CTRL_L      },
+       { XFUNC_cls,                    1,  CTRL_L      },
+       { XFUNC_meta1,                  0,  CTRL_BO     },
+       { XFUNC_meta2,                  0,  CTRL_X      },
+       { XFUNC_kill,                   0,  CTRL_K      },
+       { XFUNC_yank,                   0,  CTRL_Y      },
        { XFUNC_meta_yank,              1,      'y'     },
-       { XFUNC_literal,                0, CTRL('^')    },
+       { XFUNC_literal,                0,  CTRL_CA     },
        { XFUNC_comment,                1,      '#'     },
-       { XFUNC_transpose,              0, CTRL('T')    },
-       { XFUNC_complete,               1, CTRL('[')    },
-       { XFUNC_comp_list,              0, CTRL('I')    },
+       { XFUNC_transpose,              0,  CTRL_T      },
+       { XFUNC_complete,               1,  CTRL_BO     },
+       { XFUNC_comp_list,              0,  CTRL_I      },
        { XFUNC_comp_list,              1,      '='     },
        { XFUNC_enumerate,              1,      '?'     },
        { XFUNC_expand,                 1,      '*'     },
-       { XFUNC_comp_file,              1, CTRL('X')    },
-       { XFUNC_comp_comm,              2, CTRL('[')    },
+       { XFUNC_comp_file,              1,  CTRL_X      },
+       { XFUNC_comp_comm,              2,  CTRL_BO     },
        { XFUNC_list_comm,              2,      '?'     },
-       { XFUNC_list_file,              2, CTRL('Y')    },
+       { XFUNC_list_file,              2,  CTRL_Y      },
        { XFUNC_set_mark,               1,      ' '     },
-       { XFUNC_kill_region,            0, CTRL('W')    },
-       { XFUNC_xchg_point_mark,        2, CTRL('X')    },
-       { XFUNC_literal,                0, CTRL('V')    },
-       { XFUNC_version,                1, CTRL('V')    },
+       { XFUNC_kill_region,            0,  CTRL_W      },
+       { XFUNC_xchg_point_mark,        2,  CTRL_X      },
+       { XFUNC_literal,                0,  CTRL_V      },
+       { XFUNC_version,                1,  CTRL_V      },
        { XFUNC_prev_histword,          1,      '.'     },
        { XFUNC_prev_histword,          1,      '_'     },
        { XFUNC_set_arg,                1,      '0'     },
@@ -1148,7 +1144,7 @@ static struct x_defbindings const x_defbindings[] = {
 #endif
 #ifndef MKSH_SMALL
        /* more non-standard ones */
-       { XFUNC_eval_region,            1, CTRL('E')    },
+       { XFUNC_eval_region,            1,  CTRL_E      },
        { XFUNC_edit_line,              2,      'e'     }
 #endif
 };
@@ -1191,17 +1187,19 @@ x_e_getmbc(char *sbuf)
        if (c == -1)
                return (-1);
        if (UTFMODE) {
-               if ((buf[0] >= 0xC2) && (buf[0] < 0xF0)) {
+               if ((rtt2asc(buf[0]) >= (unsigned char)0xC2) &&
+                   (rtt2asc(buf[0]) < (unsigned char)0xF0)) {
                        c = x_e_getc();
                        if (c == -1)
                                return (-1);
-                       if ((c & 0xC0) != 0x80) {
+                       if ((rtt2asc(c) & 0xC0) != 0x80) {
                                x_e_ungetc(c);
                                return (1);
                        }
                        buf[pos++] = c;
                }
-               if ((buf[0] >= 0xE0) && (buf[0] < 0xF0)) {
+               if ((rtt2asc(buf[0]) >= (unsigned char)0xE0) &&
+                   (rtt2asc(buf[0]) < (unsigned char)0xF0)) {
                        /* XXX x_e_ungetc is one-octet only */
                        buf[pos++] = c = x_e_getc();
                        if (c == -1)
@@ -1299,9 +1297,7 @@ x_emacs(char *buf)
                        return (i);
                case KINTR:
                        /* special case for interrupt */
-                       trapsig(SIGINT);
-                       x_mode(false);
-                       unwind(LSHELL);
+                       x_intr(SIGINT, c);
                }
                /* ad-hoc hack for fixing the cursor position */
                x_goto(xcp);
@@ -1320,11 +1316,11 @@ x_insert(int c)
        if (c == 0) {
  invmbs:
                left = 0;
-               x_e_putc2(7);
+               x_e_putc2(KSH_BEL);
                return (KSTD);
        }
        if (UTFMODE) {
-               if (((c & 0xC0) == 0x80) && left) {
+               if (((rtt2asc(c) & 0xC0) == 0x80) && left) {
                        str[pos++] = c;
                        if (!--left) {
                                str[pos] = '\0';
@@ -1382,7 +1378,7 @@ static int
 x_do_ins(const char *cp, size_t len)
 {
        if (xep + len >= xend) {
-               x_e_putc2(7);
+               x_e_putc2(KSH_BEL);
                return (-1);
        }
        memmove(xcp + len, xcp, xep - xcp + 1);
@@ -1422,7 +1418,7 @@ x_del_back(int c MKSH_A_UNUSED)
        ssize_t i = 0;
 
        if (xcp == xbuf) {
-               x_e_putc2(7);
+               x_e_putc2(KSH_BEL);
                return (KSTD);
        }
        do {
@@ -1448,7 +1444,7 @@ x_del_char(int c MKSH_A_UNUSED)
        }
 
        if (!i) {
-               x_e_putc2(7);
+               x_e_putc2(KSH_BEL);
                return (KSTD);
        }
        x_delete(i, false);
@@ -1558,15 +1554,15 @@ x_bword(void)
        char *cp = xcp;
 
        if (cp == xbuf) {
-               x_e_putc2(7);
+               x_e_putc2(KSH_BEL);
                return (0);
        }
        while (x_arg--) {
-               while (cp != xbuf && is_mfs(cp[-1])) {
+               while (cp != xbuf && ctype(cp[-1], C_MFS)) {
                        cp--;
                        nb++;
                }
-               while (cp != xbuf && !is_mfs(cp[-1])) {
+               while (cp != xbuf && !ctype(cp[-1], C_MFS)) {
                        cp--;
                        nb++;
                }
@@ -1582,13 +1578,13 @@ x_fword(bool move)
        char *cp = xcp;
 
        if (cp == xep) {
-               x_e_putc2(7);
+               x_e_putc2(KSH_BEL);
                return (0);
        }
        while (x_arg--) {
-               while (cp != xep && is_mfs(*cp))
+               while (cp != xep && ctype(*cp, C_MFS))
                        cp++;
-               while (cp != xep && !is_mfs(*cp))
+               while (cp != xep && !ctype(*cp, C_MFS))
                        cp++;
        }
        nc = x_nb2nc(cp - xcp);
@@ -1621,7 +1617,7 @@ x_bs0(char *cp, char *lower_bound)
 {
        if (UTFMODE)
                while ((!lower_bound || (cp > lower_bound)) &&
-                   ((*(unsigned char *)cp & 0xC0) == 0x80))
+                   ((rtt2asc(*cp) & 0xC0) == 0x80))
                        --cp;
        return (cp);
 }
@@ -1642,14 +1638,14 @@ x_size2(char *cp, char **dcp)
 {
        uint8_t c = *(unsigned char *)cp;
 
-       if (UTFMODE && (c > 0x7F))
+       if (UTFMODE && (rtt2asc(c) > 0x7F))
                return (utf_widthadj(cp, (const char **)dcp));
        if (dcp)
                *dcp = cp + 1;
        if (c == '\t')
                /* Kludge, tabs are always four spaces. */
                return (4);
-       if (ISCTRL(c) && /* but not C1 */ c < 0x80)
+       if (ksh_isctrl(c))
                /* control unsigned char */
                return (2);
        return (1);
@@ -1674,9 +1670,9 @@ x_zotc3(char **cp)
                /* Kludge, tabs are always four spaces. */
                x_e_puts(T4spaces);
                (*cp)++;
-       } else if (ISCTRL(c) && /* but not C1 */ c < 0x80) {
+       } else if (ksh_isctrl(c)) {
                x_e_putc2('^');
-               x_e_putc2(UNCTRL(c));
+               x_e_putc2(ksh_unctrl(c));
                (*cp)++;
        } else
                x_e_putc3((const char **)cp);
@@ -1686,7 +1682,7 @@ static int
 x_mv_back(int c MKSH_A_UNUSED)
 {
        if (xcp == xbuf) {
-               x_e_putc2(7);
+               x_e_putc2(KSH_BEL);
                return (KSTD);
        }
        while (x_arg--) {
@@ -1703,7 +1699,7 @@ x_mv_forw(int c MKSH_A_UNUSED)
        char *cp = xcp, *cp2;
 
        if (xcp == xep) {
-               x_e_putc2(7);
+               x_e_putc2(KSH_BEL);
                return (KSTD);
        }
        while (x_arg--) {
@@ -1724,13 +1720,13 @@ x_search_char_forw(int c MKSH_A_UNUSED)
 
        *xep = '\0';
        if (x_e_getmbc(tmp) < 0) {
-               x_e_putc2(7);
+               x_e_putc2(KSH_BEL);
                return (KSTD);
        }
        while (x_arg--) {
                if ((cp = (cp == xep) ? NULL : strstr(cp + 1, tmp)) == NULL &&
                    (cp = strstr(xbuf, tmp)) == NULL) {
-                       x_e_putc2(7);
+                       x_e_putc2(KSH_BEL);
                        return (KSTD);
                }
        }
@@ -1745,7 +1741,7 @@ x_search_char_back(int c MKSH_A_UNUSED)
        bool b;
 
        if (x_e_getmbc(tmp) < 0) {
-               x_e_putc2(7);
+               x_e_putc2(KSH_BEL);
                return (KSTD);
        }
        for (; x_arg--; cp = p)
@@ -1753,7 +1749,7 @@ x_search_char_back(int c MKSH_A_UNUSED)
                        if (p-- == xbuf)
                                p = xep;
                        if (p == cp) {
-                               x_e_putc2(7);
+                               x_e_putc2(KSH_BEL);
                                return (KSTD);
                        }
                        if ((tmp[1] && ((p+1) > xep)) ||
@@ -1789,7 +1785,7 @@ x_end_of_text(int c MKSH_A_UNUSED)
        unsigned char tmp[1], *cp = tmp;
 
        *tmp = isedchar(edchars.eof) ? (unsigned char)edchars.eof :
-           (unsigned char)CTRL('D');
+           (unsigned char)CTRL_D;
        x_zotc3((char **)&cp);
        x_putc('\r');
        x_putc('\n');
@@ -1849,7 +1845,7 @@ x_load_hist(char **hp)
                sp = holdbufp;
                modified = 0;
        } else if (hp < history || hp > histptr) {
-               x_e_putc2(7);
+               x_e_putc2(KSH_BEL);
                return;
        }
        if (sp == NULL)
@@ -1859,7 +1855,7 @@ x_load_hist(char **hp)
                strlcpy(holdbufp, xbuf, LINE);
        strlcpy(xbuf, sp, xend - xbuf);
        xbp = xbuf;
-       xep = xcp = xbuf + strlen(xbuf);
+       xep = xcp = strnul(xbuf);
        x_adjust();
        modified = 0;
 }
@@ -1904,13 +1900,13 @@ x_search_hist(int c)
                if ((c = x_e_getc()) < 0)
                        return (KSTD);
                f = x_tab[0][c];
-               if (c == CTRL('[')) {
+               if (c == CTRL_BO) {
                        if ((f & 0x7F) == XFUNC_meta1) {
                                if ((c = x_e_getc()) < 0)
                                        return (KSTD);
                                f = x_tab[1][c] & 0x7F;
                                if (f == XFUNC_meta1 || f == XFUNC_meta2)
-                                       x_meta1(CTRL('['));
+                                       x_meta1(CTRL_BO);
                                x_e_ungetc(c);
                        }
                        break;
@@ -1942,7 +1938,7 @@ x_search_hist(int c)
                        /* add char to pattern */
                        /* overflow check... */
                        if ((size_t)(p - pat) >= sizeof(pat) - 1) {
-                               x_e_putc2(7);
+                               x_e_putc2(KSH_BEL);
                                continue;
                        }
                        *p++ = c, *p = '\0';
@@ -1988,7 +1984,7 @@ x_search(char *pat, int sameline, int offset)
                        return (i);
                }
        }
-       x_e_putc2(7);
+       x_e_putc2(KSH_BEL);
        x_histp = histptr;
        return (-1);
 }
@@ -2094,7 +2090,7 @@ x_clrtoeol(int lastch, bool line_was_cleared)
        int col;
 
        if (lastch == ' ' && !line_was_cleared && x_term_mode == 1) {
-               shf_puts("\033[K", shl_out);
+               shf_puts(KSH_ESC_STRING "[K", shl_out);
                line_was_cleared = true;
        }
        if (lastch == ' ' && line_was_cleared)
@@ -2168,11 +2164,11 @@ x_transpose(int c MKSH_A_UNUSED)
         * to the one they want.
         */
        if (xcp == xbuf) {
-               x_e_putc2(7);
+               x_e_putc2(KSH_BEL);
                return (KSTD);
        } else if (xcp == xep || Flag(FGMACS)) {
                if (xcp - xbuf == 1) {
-                       x_e_putc2(7);
+                       x_e_putc2(KSH_BEL);
                        return (KSTD);
                }
                /*
@@ -2181,12 +2177,12 @@ x_transpose(int c MKSH_A_UNUSED)
                 */
                x_bs3(&xcp);
                if (utf_mbtowc(&tmpa, xcp) == (size_t)-1) {
-                       x_e_putc2(7);
+                       x_e_putc2(KSH_BEL);
                        return (KSTD);
                }
                x_bs3(&xcp);
                if (utf_mbtowc(&tmpb, xcp) == (size_t)-1) {
-                       x_e_putc2(7);
+                       x_e_putc2(KSH_BEL);
                        return (KSTD);
                }
                utf_wctomb(xcp, tmpa);
@@ -2199,12 +2195,12 @@ x_transpose(int c MKSH_A_UNUSED)
                 * cursor, move cursor position along one.
                 */
                if (utf_mbtowc(&tmpa, xcp) == (size_t)-1) {
-                       x_e_putc2(7);
+                       x_e_putc2(KSH_BEL);
                        return (KSTD);
                }
                x_bs3(&xcp);
                if (utf_mbtowc(&tmpb, xcp) == (size_t)-1) {
-                       x_e_putc2(7);
+                       x_e_putc2(KSH_BEL);
                        return (KSTD);
                }
                utf_wctomb(xcp, tmpa);
@@ -2313,21 +2309,35 @@ x_meta_yank(int c MKSH_A_UNUSED)
        return (KSTD);
 }
 
-static int
-x_abort(int c MKSH_A_UNUSED)
+/* fake receiving an interrupt */
+static void
+x_intr(int signo, int c)
 {
-       /* x_zotc(c); */
+       x_vi_zotc(c);
+       *xep = '\0';
+       strip_nuls(xbuf, xep - xbuf);
+       if (*xbuf)
+               histsave(&source->line, xbuf, HIST_STORE, true);
        xlp = xep = xcp = xbp = xbuf;
        xlp_valid = true;
        *xcp = 0;
        x_modified();
+       x_flush();
+       trapsig(signo);
+       x_mode(false);
+       unwind(LSHELL);
+}
+
+static int
+x_abort(int c MKSH_A_UNUSED)
+{
        return (KINTR);
 }
 
 static int
 x_error(int c MKSH_A_UNUSED)
 {
-       x_e_putc2(7);
+       x_e_putc2(KSH_BEL);
        return (KSTD);
 }
 
@@ -2387,19 +2397,18 @@ x_mapin(const char *cp, Area *ap)
        strdupx(news, cp, ap);
        op = news;
        while (*cp) {
-               /* XXX -- should handle \^ escape? */
-               if (*cp == '^') {
+               switch (*cp) {
+               case '^':
                        cp++;
-                       /*XXX or ^^ escape? this is ugly. */
-                       if (*cp >= '?')
-                               /* includes '?'; ASCII */
-                               *op++ = CTRL(*cp);
-                       else {
-                               *op++ = '^';
-                               cp--;
-                       }
-               } else
+                       *op++ = ksh_toctrl(*cp);
+                       break;
+               case '\\':
+                       if (cp[1] == '\\' || cp[1] == '^')
+                               ++cp;
+                       /* FALLTHROUGH */
+               default:
                        *op++ = *cp;
+               }
                cp++;
        }
        *op = '\0';
@@ -2412,9 +2421,9 @@ x_mapout2(int c, char **buf)
 {
        char *p = *buf;
 
-       if (ISCTRL(c)) {
+       if (ksh_isctrl(c)) {
                *p++ = '^';
-               *p++ = UNCTRL(c);
+               *p++ = ksh_unctrl(c);
        } else
                *p++ = c;
        *p = 0;
@@ -2437,9 +2446,9 @@ x_print(int prefix, int key)
        int f = x_tab[prefix][key];
 
        if (prefix)
-               /* prefix == 1 || prefix == 2 */
-               shf_puts(x_mapout(prefix == 1 ? CTRL('[') :
-                   prefix == 2 ? CTRL('X') : 0), shl_stdout);
+               /* prefix == 1 || prefix == 2 || prefix == 3 */
+               shf_puts(x_mapout(prefix == 1 ? CTRL_BO :
+                   prefix == 2 ? CTRL_X : 0), shl_stdout);
 #ifdef MKSH_SMALL
        shprintf("%s = ", x_mapout(key));
 #else
@@ -2603,7 +2612,7 @@ x_kill_region(int c MKSH_A_UNUSED)
        char *xr;
 
        if (xmp == NULL) {
-               x_e_putc2(7);
+               x_e_putc2(KSH_BEL);
                return (KSTD);
        }
        if (xmp > xcp) {
@@ -2625,7 +2634,7 @@ x_xchg_point_mark(int c MKSH_A_UNUSED)
        char *tmp;
 
        if (xmp == NULL) {
-               x_e_putc2(7);
+               x_e_putc2(KSH_BEL);
                return (KSTD);
        }
        tmp = xmp;
@@ -2703,7 +2712,7 @@ x_expand(int c MKSH_A_UNUSED)
            &start, &end, &words);
 
        if (nwords == 0) {
-               x_e_putc2(7);
+               x_e_putc2(KSH_BEL);
                return (KSTD);
        }
        x_goto(xbuf + start);
@@ -2713,7 +2722,7 @@ x_expand(int c MKSH_A_UNUSED)
        while (i < nwords) {
                if (x_escape(words[i], strlen(words[i]), x_do_ins) < 0 ||
                    (++i < nwords && x_ins(T1space) < 0)) {
-                       x_e_putc2(7);
+                       x_e_putc2(KSH_BEL);
                        return (KSTD);
                }
        }
@@ -2737,7 +2746,7 @@ do_complete(
            &start, &end, &words);
        /* no match */
        if (nwords == 0) {
-               x_e_putc2(7);
+               x_e_putc2(KSH_BEL);
                return;
        }
        if (type == CT_LIST) {
@@ -2894,9 +2903,10 @@ x_e_putc2(int c)
 {
        int width = 1;
 
-       if (c == '\r' || c == '\n')
+       if (ctype(c, C_CR | C_LF))
                x_col = 0;
        if (x_col < xx_cols) {
+#ifndef MKSH_EBCDIC
                if (UTFMODE && (c > 0x7F)) {
                        char utf_tmp[3];
                        size_t x;
@@ -2911,9 +2921,10 @@ x_e_putc2(int c)
                                x_putc(utf_tmp[2]);
                        width = utf_wcwidth(c);
                } else
+#endif
                        x_putc(c);
                switch (c) {
-               case 7:
+               case KSH_BEL:
                        break;
                case '\r':
                case '\n':
@@ -2935,7 +2946,7 @@ x_e_putc3(const char **cp)
 {
        int width = 1, c = **(const unsigned char **)cp;
 
-       if (c == '\r' || c == '\n')
+       if (ctype(c, C_CR | C_LF))
                x_col = 0;
        if (x_col < xx_cols) {
                if (UTFMODE && (c > 0x7F)) {
@@ -2944,7 +2955,13 @@ x_e_putc3(const char **cp)
                        width = utf_widthadj(*cp, (const char **)&cp2);
                        if (cp2 == *cp + 1) {
                                (*cp)++;
+#ifdef MKSH_EBCDIC
+                               x_putc(asc2rtt(0xEF));
+                               x_putc(asc2rtt(0xBF));
+                               x_putc(asc2rtt(0xBD));
+#else
                                shf_puts("\xEF\xBF\xBD", shl_out);
+#endif
                        } else
                                while (*cp < cp2)
                                        x_putcf(*(*cp)++);
@@ -2953,7 +2970,7 @@ x_e_putc3(const char **cp)
                        x_putc(c);
                }
                switch (c) {
-               case 7:
+               case KSH_BEL:
                        break;
                case '\r':
                case '\n':
@@ -2997,7 +3014,7 @@ x_set_arg(int c)
 
        /* strip command prefix */
        c &= 255;
-       while (c >= 0 && ksh_isdigit(c)) {
+       while (c >= 0 && ctype(c, C_DIGIT)) {
                n = n * 10 + ksh_numdig(c);
                if (n > LINE)
                        /* upper bound for repeat */
@@ -3007,7 +3024,7 @@ x_set_arg(int c)
        }
        if (c < 0 || first) {
  x_set_arg_too_big:
-               x_e_putc2(7);
+               x_e_putc2(KSH_BEL);
                x_arg = 1;
                x_arg_defaulted = true;
        } else {
@@ -3026,7 +3043,7 @@ x_comment(int c MKSH_A_UNUSED)
        int ret = x_do_comment(xbuf, xend - xbuf, &len);
 
        if (ret < 0)
-               x_e_putc2(7);
+               x_e_putc2(KSH_BEL);
        else {
                x_modified();
                xep = xbuf + len;
@@ -3049,7 +3066,7 @@ x_version(int c MKSH_A_UNUSED)
        strdupx(v, KSH_VERSION, ATEMP);
 
        xbuf = xbp = xcp = v;
-       xend = xep = v + strlen(v);
+       xend = xep = strnul(v);
        x_redraw('\r');
        x_flush();
 
@@ -3077,7 +3094,7 @@ x_edit_line(int c MKSH_A_UNUSED)
 {
        if (x_arg_defaulted) {
                if (xep == xbuf) {
-                       x_e_putc2(7);
+                       x_e_putc2(KSH_BEL);
                        return (KSTD);
                }
                if (modified) {
@@ -3092,7 +3109,7 @@ x_edit_line(int c MKSH_A_UNUSED)
                    "fc -e ${VISUAL:-${EDITOR:-vi}} --", x_arg);
        else
                strlcpy(xbuf, "fc -e ${VISUAL:-${EDITOR:-vi}} --", xend - xbuf);
-       xep = xbuf + strlen(xbuf);
+       xep = strnul(xbuf);
        return (x_newline('\n'));
 }
 #endif
@@ -3132,7 +3149,7 @@ x_prev_histword(int c MKSH_A_UNUSED)
                last_arg = x_arg_defaulted ? -1 : x_arg;
        xhp = histptr - (m - 1);
        if ((xhp < history) || !(cp = *xhp)) {
-               x_e_putc2(7);
+               x_e_putc2(KSH_BEL);
                x_modified();
                return (KSTD);
        }
@@ -3144,11 +3161,11 @@ x_prev_histword(int c MKSH_A_UNUSED)
                /*
                 * ignore white-space after the last word
                 */
-               while (rcp > cp && is_cfs(*rcp))
+               while (rcp > cp && ctype(*rcp, C_CFS))
                        rcp--;
-               while (rcp > cp && !is_cfs(*rcp))
+               while (rcp > cp && !ctype(*rcp, C_CFS))
                        rcp--;
-               if (is_cfs(*rcp))
+               if (ctype(*rcp, C_CFS))
                        rcp++;
                x_ins(rcp);
        } else {
@@ -3159,16 +3176,16 @@ x_prev_histword(int c MKSH_A_UNUSED)
                /*
                 * ignore white-space at start of line
                 */
-               while (*rcp && is_cfs(*rcp))
+               while (*rcp && ctype(*rcp, C_CFS))
                        rcp++;
                while (x_arg-- > 0) {
-                       while (*rcp && !is_cfs(*rcp))
+                       while (*rcp && !ctype(*rcp, C_CFS))
                                rcp++;
-                       while (*rcp && is_cfs(*rcp))
+                       while (*rcp && ctype(*rcp, C_CFS))
                                rcp++;
                }
                cp = rcp;
-               while (*rcp && !is_cfs(*rcp))
+               while (*rcp && !ctype(*rcp, C_CFS))
                        rcp++;
                ch = *rcp;
                *rcp = '\0';
@@ -3220,14 +3237,14 @@ x_fold_case(int c)
        char *cp = xcp;
 
        if (cp == xep) {
-               x_e_putc2(7);
+               x_e_putc2(KSH_BEL);
                return (KSTD);
        }
        while (x_arg--) {
                /*
                 * first skip over any white-space
                 */
-               while (cp != xep && is_mfs(*cp))
+               while (cp != xep && ctype(*cp, C_MFS))
                        cp++;
                /*
                 * do the first char on its own since it may be
@@ -3245,7 +3262,7 @@ x_fold_case(int c)
                /*
                 * now for the rest of the word
                 */
-               while (cp != xep && !is_mfs(*cp)) {
+               while (cp != xep && !ctype(*cp, C_MFS)) {
                        if (c == 'U')
                                /* uppercase */
                                *cp = ksh_toupper(*cp);
@@ -3324,17 +3341,17 @@ x_mode(bool onoff)
 #endif
 
                if (!edchars.erase)
-                       edchars.erase = CTRL('H');
+                       edchars.erase = CTRL_H;
                if (!edchars.kill)
-                       edchars.kill = CTRL('U');
+                       edchars.kill = CTRL_U;
                if (!edchars.intr)
-                       edchars.intr = CTRL('C');
+                       edchars.intr = CTRL_C;
                if (!edchars.quit)
-                       edchars.quit = CTRL('\\');
+                       edchars.quit = CTRL_BK;
                if (!edchars.eof)
-                       edchars.eof = CTRL('D');
+                       edchars.eof = CTRL_D;
                if (!edchars.werase)
-                       edchars.werase = CTRL('W');
+                       edchars.werase = CTRL_W;
 
                if (isedchar(edchars.erase)) {
                        bind_if_not_bound(0, edchars.erase, XFUNC_del_back);
@@ -3368,6 +3385,7 @@ static int nextstate(int);
 static int vi_insert(int);
 static int vi_cmd(int, const char *);
 static int domove(int, const char *, int);
+static int domovebeg(void);
 static int redo_insert(int);
 static void yank_range(int, int);
 static int bracktype(int);
@@ -3394,12 +3412,10 @@ static void ed_mov_opt(int, char *);
 static int expand_word(int);
 static int complete_word(int, int);
 static int print_expansions(struct edstate *, int);
-#define char_len(c)    ((ISCTRL((unsigned char)c) && \
-                       /* but not C1 */ (unsigned char)c < 0x80) ? 2 : 1)
-static void x_vi_zotc(int);
 static void vi_error(void);
 static void vi_macro_reset(void);
 static int x_vi_putbuf(const char *, size_t);
+#define char_len(c) (ksh_isctrl(c) ? 2 : 1)
 
 #define vC     0x01            /* a valid command that isn't a vM, vE, vU */
 #define vM     0x02            /* movement command (h, l, etc.) */
@@ -3410,14 +3426,14 @@ static int x_vi_putbuf(const char *, size_t);
 #define vZ     0x40            /* repeat count defaults to 0 (not 1) */
 #define vS     0x80            /* search (/, ?) */
 
-#define is_bad(c)      (classify[(c)&0x7f]&vB)
-#define is_cmd(c)      (classify[(c)&0x7f]&(vM|vE|vC|vU))
-#define is_move(c)     (classify[(c)&0x7f]&vM)
-#define is_extend(c)   (classify[(c)&0x7f]&vE)
-#define is_long(c)     (classify[(c)&0x7f]&vX)
-#define is_undoable(c) (!(classify[(c)&0x7f]&vU))
-#define is_srch(c)     (classify[(c)&0x7f]&vS)
-#define is_zerocount(c)        (classify[(c)&0x7f]&vZ)
+#define is_bad(c)      (classify[rtt2asc(c) & 0x7F] & vB)
+#define is_cmd(c)      (classify[rtt2asc(c) & 0x7F] & (vM | vE | vC | vU))
+#define is_move(c)     (classify[rtt2asc(c) & 0x7F] & vM)
+#define is_extend(c)   (classify[rtt2asc(c) & 0x7F] & vE)
+#define is_long(c)     (classify[rtt2asc(c) & 0x7F] & vX)
+#define is_undoable(c) (!(classify[rtt2asc(c) & 0x7F] & vU))
+#define is_srch(c)     (classify[rtt2asc(c) & 0x7F] & vS)
+#define is_zerocount(c)        (classify[rtt2asc(c) & 0x7F] & vZ)
 
 static const unsigned char classify[128] = {
 /*      0      1       2       3       4       5       6       7       */
@@ -3587,13 +3603,14 @@ x_vi(char *buf)
                if (state != VLIT) {
                        if (isched(c, edchars.intr) ||
                            isched(c, edchars.quit)) {
+                               /* shove input buffer away */
+                               xbuf = ebuf.cbuf;
+                               xep = xbuf;
+                               if (ebuf.linelen > 0)
+                                       xep += ebuf.linelen;
                                /* pretend we got an interrupt */
-                               x_vi_zotc(c);
-                               x_flush();
-                               trapsig(isched(c, edchars.intr) ?
-                                   SIGINT : SIGQUIT);
-                               x_mode(false);
-                               unwind(LSHELL);
+                               x_intr(isched(c, edchars.intr) ?
+                                   SIGINT : SIGQUIT, c);
                        } else if (isched(c, edchars.eof) &&
                            state != VVERSION) {
                                if (vs->linelen == 0) {
@@ -3646,7 +3663,7 @@ vi_hook(int ch)
                default: ch = 0; goto vi_insert_failed;
                }
                if (insert != 0) {
-                       if (ch == CTRL('v')) {
+                       if (ch == CTRL_V) {
                                state = VLIT;
                                ch = '^';
                        }
@@ -3667,11 +3684,11 @@ vi_hook(int ch)
                                return (1);
                        }
                } else {
-                       if (ch == '\r' || ch == '\n')
+                       if (ctype(ch, C_CR | C_LF))
                                return (1);
                        cmdlen = 0;
                        argc1 = 0;
-                       if (ch >= ord('1') && ch <= ord('9')) {
+                       if (ctype(ch, C_DIGIT) && ord(ch) != ord('0')) {
                                argc1 = ksh_numdig(ch);
                                state = VARG1;
                        } else {
@@ -3716,7 +3733,7 @@ vi_hook(int ch)
                break;
 
        case VARG1:
-               if (ksh_isdigit(ch))
+               if (ctype(ch, C_DIGIT))
                        argc1 = argc1 * 10 + ksh_numdig(ch);
                else {
                        curcmd[cmdlen++] = ch;
@@ -3726,7 +3743,7 @@ vi_hook(int ch)
 
        case VEXTCMD:
                argc2 = 0;
-               if (ch >= ord('1') && ch <= ord('9')) {
+               if (ctype(ch, C_DIGIT) && ord(ch) != ord('0')) {
                        argc2 = ksh_numdig(ch);
                        state = VARG2;
                        return (0);
@@ -3742,7 +3759,7 @@ vi_hook(int ch)
                break;
 
        case VARG2:
-               if (ksh_isdigit(ch))
+               if (ctype(ch, C_DIGIT))
                        argc2 = argc2 * 10 + ksh_numdig(ch);
                else {
                        if (argc1 == 0)
@@ -3760,7 +3777,7 @@ vi_hook(int ch)
                break;
 
        case VXCH:
-               if (ch == CTRL('['))
+               if (ch == CTRL_BO)
                        state = VNORMAL;
                else {
                        curcmd[cmdlen++] = ch;
@@ -3769,7 +3786,7 @@ vi_hook(int ch)
                break;
 
        case VSEARCH:
-               if (ch == '\r' || ch == '\n' /*|| ch == CTRL('[')*/ ) {
+               if (ctype(ch, C_CR | C_LF) /* || ch == CTRL_BO */ ) {
                        restore_cbuf();
                        /* Repeat last search? */
                        if (srchlen == 0) {
@@ -3784,7 +3801,7 @@ vi_hook(int ch)
                                memcpy(srchpat, locpat, srchlen + 1);
                        }
                        state = VCMD;
-               } else if (isched(ch, edchars.erase) || ch == CTRL('h')) {
+               } else if (isched(ch, edchars.erase) || ch == CTRL_H) {
                        if (srchlen != 0) {
                                srchlen--;
                                vs->linelen -= char_len(locpat[srchlen]);
@@ -3825,12 +3842,12 @@ vi_hook(int ch)
                                vi_error();
                        else {
                                locpat[srchlen++] = ch;
-                               if (ISCTRL(ch) && /* but not C1 */ ch < 0x80) {
+                               if (ksh_isctrl(ch)) {
                                        if ((size_t)vs->linelen + 2 >
                                            (size_t)vs->cbufsize)
                                                vi_error();
                                        vs->cbuf[vs->linelen++] = '^';
-                                       vs->cbuf[vs->linelen++] = UNCTRL(ch);
+                                       vs->cbuf[vs->linelen++] = ksh_unctrl(ch);
                                } else {
                                        if (vs->linelen >= vs->cbufsize)
                                                vi_error();
@@ -3903,8 +3920,8 @@ vi_hook(int ch)
                        break;
                case 0:
                        if (insert != 0) {
-                               if (lastcmd[0] == 's' || lastcmd[0] == 'c' ||
-                                   lastcmd[0] == 'C') {
+                               if (lastcmd[0] == 's' ||
+                                   ksh_eq(lastcmd[0], 'C', 'c')) {
                                        if (redo_insert(1) != 0)
                                                vi_error();
                                } else {
@@ -3942,7 +3959,7 @@ nextstate(int ch)
                return (VXCH);
        else if (ch == '.')
                return (VREDO);
-       else if (ch == CTRL('v'))
+       else if (ch == CTRL_V)
                return (VVERSION);
        else if (is_cmd(ch))
                return (VCMD);
@@ -3955,7 +3972,7 @@ vi_insert(int ch)
 {
        int tcursor;
 
-       if (isched(ch, edchars.erase) || ch == CTRL('h')) {
+       if (isched(ch, edchars.erase) || ch == CTRL_H) {
                if (insert == REPLACE) {
                        if (vs->cursor == undo->cursor) {
                                vi_error();
@@ -4012,7 +4029,7 @@ vi_insert(int ch)
         * buffer (if user inserts & deletes char, ibuf gets trashed and
         * we don't want to use it)
         */
-       if (first_insert && ch != CTRL('['))
+       if (first_insert && ch != CTRL_BO)
                saved_inslen = 0;
        switch (ch) {
        case '\0':
@@ -4022,7 +4039,7 @@ vi_insert(int ch)
        case '\n':
                return (1);
 
-       case CTRL('['):
+       case CTRL_BO:
                expanded = NONE;
                if (first_insert) {
                        first_insert = false;
@@ -4033,26 +4050,25 @@ vi_insert(int ch)
                        lastcmd[0] = 'a';
                        lastac = 1;
                }
-               if (lastcmd[0] == 's' || lastcmd[0] == 'c' ||
-                   lastcmd[0] == 'C')
+               if (lastcmd[0] == 's' || ksh_eq(lastcmd[0], 'C', 'c'))
                        return (redo_insert(0));
                else
                        return (redo_insert(lastac - 1));
 
        /* { start nonstandard vi commands */
-       case CTRL('x'):
+       case CTRL_X:
                expand_word(0);
                break;
 
-       case CTRL('f'):
+       case CTRL_F:
                complete_word(0, 0);
                break;
 
-       case CTRL('e'):
+       case CTRL_E:
                print_expansions(vs, 0);
                break;
 
-       case CTRL('i'):
+       case CTRL_I:
                if (Flag(FVITABCOMPLETE)) {
                        complete_word(0, 0);
                        break;
@@ -4105,14 +4121,14 @@ vi_cmd(int argcnt, const char *cmd)
                        lastac = argcnt;
                        memmove(lastcmd, cmd, MAXVICMD);
                }
-               switch (*cmd) {
+               switch (ord(*cmd)) {
 
-               case CTRL('l'):
-               case CTRL('r'):
+               case CTRL_L:
+               case CTRL_R:
                        redraw_line(true);
                        break;
 
-               case '@':
+               case ord('@'):
                        {
                                static char alias[] = "_\0";
                                struct tbl *ap;
@@ -4153,7 +4169,7 @@ vi_cmd(int argcnt, const char *cmd)
                        }
                        break;
 
-               case 'a':
+               case ord('a'):
                        modified = 1;
                        hnum = hlast;
                        if (vs->linelen != 0)
@@ -4161,7 +4177,7 @@ vi_cmd(int argcnt, const char *cmd)
                        insert = INSERT;
                        break;
 
-               case 'A':
+               case ord('A'):
                        modified = 1;
                        hnum = hlast;
                        del_range(0, 0);
@@ -4169,36 +4185,35 @@ vi_cmd(int argcnt, const char *cmd)
                        insert = INSERT;
                        break;
 
-               case 'S':
-                       vs->cursor = domove(1, "^", 1);
+               case ord('S'):
+                       vs->cursor = domovebeg();
                        del_range(vs->cursor, vs->linelen);
                        modified = 1;
                        hnum = hlast;
                        insert = INSERT;
                        break;
 
-               case 'Y':
+               case ord('Y'):
                        cmd = "y$";
                        /* ahhhhhh... */
 
                        /* FALLTHROUGH */
-               case 'c':
-               case 'd':
-               case 'y':
+               case ord('c'):
+               case ord('d'):
+               case ord('y'):
                        if (*cmd == cmd[1]) {
-                               c1 = *cmd == 'c' ? domove(1, "^", 1) : 0;
+                               c1 = *cmd == 'c' ? domovebeg() : 0;
                                c2 = vs->linelen;
                        } else if (!is_move(cmd[1]))
                                return (-1);
                        else {
                                if ((ncursor = domove(argcnt, &cmd[1], 1)) < 0)
                                        return (-1);
-                               if (*cmd == 'c' &&
-                                   (cmd[1] == 'w' || cmd[1] == 'W') &&
-                                   !ksh_isspace(vs->cbuf[vs->cursor])) {
+                               if (*cmd == 'c' && ksh_eq(cmd[1], 'W', 'w') &&
+                                   !ctype(vs->cbuf[vs->cursor], C_SPACE)) {
                                        do {
                                                --ncursor;
-                                       } while (ksh_isspace(vs->cbuf[ncursor]));
+                                       } while (ctype(vs->cbuf[ncursor], C_SPACE));
                                        ncursor++;
                                }
                                if (ncursor > vs->cursor) {
@@ -4224,7 +4239,7 @@ vi_cmd(int argcnt, const char *cmd)
                        }
                        break;
 
-               case 'p':
+               case ord('p'):
                        modified = 1;
                        hnum = hlast;
                        if (vs->linelen != 0)
@@ -4238,7 +4253,7 @@ vi_cmd(int argcnt, const char *cmd)
                                return (-1);
                        break;
 
-               case 'P':
+               case ord('P'):
                        modified = 1;
                        hnum = hlast;
                        any = 0;
@@ -4251,25 +4266,25 @@ vi_cmd(int argcnt, const char *cmd)
                                return (-1);
                        break;
 
-               case 'C':
+               case ord('C'):
                        modified = 1;
                        hnum = hlast;
                        del_range(vs->cursor, vs->linelen);
                        insert = INSERT;
                        break;
 
-               case 'D':
+               case ord('D'):
                        yank_range(vs->cursor, vs->linelen);
                        del_range(vs->cursor, vs->linelen);
                        if (vs->cursor != 0)
                                vs->cursor--;
                        break;
 
-               case 'g':
+               case ord('g'):
                        if (!argcnt)
                                argcnt = hlast;
                        /* FALLTHROUGH */
-               case 'G':
+               case ord('G'):
                        if (!argcnt)
                                argcnt = 1;
                        else
@@ -4282,22 +4297,22 @@ vi_cmd(int argcnt, const char *cmd)
                        }
                        break;
 
-               case 'i':
+               case ord('i'):
                        modified = 1;
                        hnum = hlast;
                        insert = INSERT;
                        break;
 
-               case 'I':
+               case ord('I'):
                        modified = 1;
                        hnum = hlast;
-                       vs->cursor = domove(1, "^", 1);
+                       vs->cursor = domovebeg();
                        insert = INSERT;
                        break;
 
-               case 'j':
-               case '+':
-               case CTRL('n'):
+               case ord('j'):
+               case ord('+'):
+               case CTRL_N:
                        if (grabhist(modified, hnum + argcnt) < 0)
                                return (-1);
                        else {
@@ -4306,9 +4321,9 @@ vi_cmd(int argcnt, const char *cmd)
                        }
                        break;
 
-               case 'k':
-               case '-':
-               case CTRL('p'):
+               case ord('k'):
+               case ord('-'):
+               case CTRL_P:
                        if (grabhist(modified, hnum - argcnt) < 0)
                                return (-1);
                        else {
@@ -4317,7 +4332,7 @@ vi_cmd(int argcnt, const char *cmd)
                        }
                        break;
 
-               case 'r':
+               case ord('r'):
                        if (vs->linelen == 0)
                                return (-1);
                        modified = 1;
@@ -4335,13 +4350,13 @@ vi_cmd(int argcnt, const char *cmd)
                        }
                        break;
 
-               case 'R':
+               case ord('R'):
                        modified = 1;
                        hnum = hlast;
                        insert = REPLACE;
                        break;
 
-               case 's':
+               case ord('s'):
                        if (vs->linelen == 0)
                                return (-1);
                        modified = 1;
@@ -4352,7 +4367,7 @@ vi_cmd(int argcnt, const char *cmd)
                        insert = INSERT;
                        break;
 
-               case 'v':
+               case ord('v'):
                        if (!argcnt) {
                                if (vs->linelen == 0)
                                        return (-1);
@@ -4375,7 +4390,7 @@ vi_cmd(int argcnt, const char *cmd)
                        vs->linelen = strlen(vs->cbuf);
                        return (2);
 
-               case 'x':
+               case ord('x'):
                        if (vs->linelen == 0)
                                return (-1);
                        modified = 1;
@@ -4386,7 +4401,7 @@ vi_cmd(int argcnt, const char *cmd)
                        del_range(vs->cursor, vs->cursor + argcnt);
                        break;
 
-               case 'X':
+               case ord('X'):
                        if (vs->cursor > 0) {
                                modified = 1;
                                hnum = hlast;
@@ -4399,13 +4414,13 @@ vi_cmd(int argcnt, const char *cmd)
                                return (-1);
                        break;
 
-               case 'u':
+               case ord('u'):
                        t = vs;
                        vs = undo;
                        undo = t;
                        break;
 
-               case 'U':
+               case ord('U'):
                        if (!modified)
                                return (-1);
                        if (grabhist(modified, ohnum) < 0)
@@ -4414,19 +4429,19 @@ vi_cmd(int argcnt, const char *cmd)
                        hnum = ohnum;
                        break;
 
-               case '?':
+               case ord('?'):
                        if (hnum == hlast)
                                hnum = -1;
                        /* ahhh */
 
                        /* FALLTHROUGH */
-               case '/':
+               case ord('/'):
                        c3 = 1;
                        srchlen = 0;
                        lastsearch = *cmd;
                        /* FALLTHROUGH */
-               case 'n':
-               case 'N':
+               case ord('n'):
+               case ord('N'):
                        if (lastsearch == ' ')
                                return (-1);
                        if (lastsearch == '?')
@@ -4453,7 +4468,7 @@ vi_cmd(int argcnt, const char *cmd)
                                return (0);
                        }
                        break;
-               case '_':
+               case ord('_'):
                        {
                                bool inspace;
                                char *p, *sp;
@@ -4461,14 +4476,13 @@ vi_cmd(int argcnt, const char *cmd)
                                if (histnum(-1) < 0)
                                        return (-1);
                                p = *histpos();
-#define issp(c)                (ksh_isspace(c) || (c) == '\n')
                                if (argcnt) {
-                                       while (*p && issp(*p))
+                                       while (ctype(*p, C_SPACE))
                                                p++;
                                        while (*p && --argcnt) {
-                                               while (*p && !issp(*p))
+                                               while (*p && !ctype(*p, C_SPACE))
                                                        p++;
-                                               while (*p && issp(*p))
+                                               while (ctype(*p, C_SPACE))
                                                        p++;
                                        }
                                        if (!*p)
@@ -4478,7 +4492,7 @@ vi_cmd(int argcnt, const char *cmd)
                                        sp = p;
                                        inspace = false;
                                        while (*p) {
-                                               if (issp(*p))
+                                               if (ctype(*p, C_SPACE))
                                                        inspace = true;
                                                else if (inspace) {
                                                        inspace = false;
@@ -4492,7 +4506,7 @@ vi_cmd(int argcnt, const char *cmd)
                                hnum = hlast;
                                if (vs->cursor != vs->linelen)
                                        vs->cursor++;
-                               while (*p && !issp(*p)) {
+                               while (*p && !ctype(*p, C_SPACE)) {
                                        argcnt++;
                                        p++;
                                }
@@ -4506,7 +4520,7 @@ vi_cmd(int argcnt, const char *cmd)
                        }
                        break;
 
-               case '~':
+               case ord('~'):
                        {
                                char *p;
                                int i;
@@ -4515,11 +4529,11 @@ vi_cmd(int argcnt, const char *cmd)
                                        return (-1);
                                for (i = 0; i < argcnt; i++) {
                                        p = &vs->cbuf[vs->cursor];
-                                       if (ksh_islower(*p)) {
+                                       if (ctype(*p, C_LOWER)) {
                                                modified = 1;
                                                hnum = hlast;
                                                *p = ksh_toupper(*p);
-                                       } else if (ksh_isupper(*p)) {
+                                       } else if (ctype(*p, C_UPPER)) {
                                                modified = 1;
                                                hnum = hlast;
                                                *p = ksh_tolower(*p);
@@ -4530,7 +4544,7 @@ vi_cmd(int argcnt, const char *cmd)
                                break;
                        }
 
-               case '#':
+               case ord('#'):
                        {
                                int ret = x_do_comment(vs->cbuf, vs->cbufsize,
                                    &vs->linelen);
@@ -4540,44 +4554,44 @@ vi_cmd(int argcnt, const char *cmd)
                        }
 
                /* AT&T ksh */
-               case '=':
+               case ord('='):
                /* Nonstandard vi/ksh */
-               case CTRL('e'):
+               case CTRL_E:
                        print_expansions(vs, 1);
                        break;
 
 
                /* Nonstandard vi/ksh */
-               case CTRL('i'):
+               case CTRL_I:
                        if (!Flag(FVITABCOMPLETE))
                                return (-1);
                        complete_word(1, argcnt);
                        break;
 
                /* some annoying AT&T kshs */
-               case CTRL('['):
+               case CTRL_BO:
                        if (!Flag(FVIESCCOMPLETE))
                                return (-1);
                        /* FALLTHROUGH */
                /* AT&T ksh */
-               case '\\':
+               case ord('\\'):
                /* Nonstandard vi/ksh */
-               case CTRL('f'):
+               case CTRL_F:
                        complete_word(1, argcnt);
                        break;
 
 
                /* AT&T ksh */
-               case '*':
+               case ord('*'):
                /* Nonstandard vi/ksh */
-               case CTRL('x'):
+               case CTRL_X:
                        expand_word(1);
                        break;
 
 
                /* mksh: cursor movement */
-               case '[':
-               case 'O':
+               case ord('['):
+               case ord('O'):
                        state = VPREFIX2;
                        if (vs->linelen != 0)
                                vs->cursor++;
@@ -4596,20 +4610,20 @@ domove(int argcnt, const char *cmd, int sub)
        int ncursor = 0, i = 0, t;
        unsigned int bcount;
 
-       switch (*cmd) {
-       case 'b':
+       switch (ord(*cmd)) {
+       case ord('b'):
                if (!sub && vs->cursor == 0)
                        return (-1);
                ncursor = backword(argcnt);
                break;
 
-       case 'B':
+       case ord('B'):
                if (!sub && vs->cursor == 0)
                        return (-1);
                ncursor = Backword(argcnt);
                break;
 
-       case 'e':
+       case ord('e'):
                if (!sub && vs->cursor + 1 >= vs->linelen)
                        return (-1);
                ncursor = endword(argcnt);
@@ -4617,7 +4631,7 @@ domove(int argcnt, const char *cmd, int sub)
                        ncursor++;
                break;
 
-       case 'E':
+       case ord('E'):
                if (!sub && vs->cursor + 1 >= vs->linelen)
                        return (-1);
                ncursor = Endword(argcnt);
@@ -4625,18 +4639,18 @@ domove(int argcnt, const char *cmd, int sub)
                        ncursor++;
                break;
 
-       case 'f':
-       case 'F':
-       case 't':
-       case 'T':
+       case ord('f'):
+       case ord('F'):
+       case ord('t'):
+       case ord('T'):
                fsavecmd = *cmd;
                fsavech = cmd[1];
                /* FALLTHROUGH */
-       case ',':
-       case ';':
+       case ord(','):
+       case ord(';'):
                if (fsavecmd == ' ')
                        return (-1);
-               i = fsavecmd == 'f' || fsavecmd == 'F';
+               i = ksh_eq(fsavecmd, 'F', 'f');
                t = fsavecmd > 'a';
                if (*cmd == ',')
                        t = !t;
@@ -4647,8 +4661,8 @@ domove(int argcnt, const char *cmd, int sub)
                        ncursor++;
                break;
 
-       case 'h':
-       case CTRL('h'):
+       case ord('h'):
+       case CTRL_H:
                if (!sub && vs->cursor == 0)
                        return (-1);
                ncursor = vs->cursor - argcnt;
@@ -4656,8 +4670,8 @@ domove(int argcnt, const char *cmd, int sub)
                        ncursor = 0;
                break;
 
-       case ' ':
-       case 'l':
+       case ord(' '):
+       case ord('l'):
                if (!sub && vs->cursor + 1 >= vs->linelen)
                        return (-1);
                if (vs->linelen != 0) {
@@ -4667,30 +4681,27 @@ domove(int argcnt, const char *cmd, int sub)
                }
                break;
 
-       case 'w':
+       case ord('w'):
                if (!sub && vs->cursor + 1 >= vs->linelen)
                        return (-1);
                ncursor = forwword(argcnt);
                break;
 
-       case 'W':
+       case ord('W'):
                if (!sub && vs->cursor + 1 >= vs->linelen)
                        return (-1);
                ncursor = Forwword(argcnt);
                break;
 
-       case '0':
+       case ord('0'):
                ncursor = 0;
                break;
 
-       case '^':
-               ncursor = 0;
-               while (ncursor < vs->linelen - 1 &&
-                   ksh_isspace(vs->cbuf[ncursor]))
-                       ncursor++;
+       case ord('^'):
+               ncursor = domovebeg();
                break;
 
-       case '|':
+       case ord('|'):
                ncursor = argcnt;
                if (ncursor > vs->linelen)
                        ncursor = vs->linelen;
@@ -4698,14 +4709,14 @@ domove(int argcnt, const char *cmd, int sub)
                        ncursor--;
                break;
 
-       case '$':
+       case ord('$'):
                if (vs->linelen != 0)
                        ncursor = vs->linelen;
                else
                        ncursor = 0;
                break;
 
-       case '%':
+       case ord('%'):
                ncursor = vs->cursor;
                while (ncursor < vs->linelen &&
                    (i = bracktype(vs->cbuf[ncursor])) == 0)
@@ -4738,6 +4749,17 @@ domove(int argcnt, const char *cmd, int sub)
 }
 
 static int
+domovebeg(void)
+{
+       int ncursor = 0;
+
+       while (ncursor < vs->linelen - 1 &&
+           ctype(vs->cbuf[ncursor], C_SPACE))
+               ncursor++;
+       return (ncursor);
+}
+
+static int
 redo_insert(int count)
 {
        while (count-- > 0)
@@ -4760,24 +4782,24 @@ yank_range(int a, int b)
 static int
 bracktype(int ch)
 {
-       switch (ch) {
+       switch (ord(ch)) {
 
-       case '(':
+       case ord('('):
                return (1);
 
-       case '[':
+       case ord('['):
                return (2);
 
-       case '{':
+       case ord('{'):
                return (3);
 
-       case ')':
+       case ord(')'):
                return (-1);
 
-       case ']':
+       case ord(']'):
                return (-2);
 
-       case '}':
+       case ord('}'):
                return (-3);
 
        default:
@@ -4912,17 +4934,16 @@ forwword(int argcnt)
 
        ncursor = vs->cursor;
        while (ncursor < vs->linelen && argcnt--) {
-               if (ksh_isalnux(vs->cbuf[ncursor]))
+               if (ctype(vs->cbuf[ncursor], C_ALNUX))
                        while (ncursor < vs->linelen &&
-                           ksh_isalnux(vs->cbuf[ncursor]))
+                           ctype(vs->cbuf[ncursor], C_ALNUX))
                                ncursor++;
-               else if (!ksh_isspace(vs->cbuf[ncursor]))
+               else if (!ctype(vs->cbuf[ncursor], C_SPACE))
                        while (ncursor < vs->linelen &&
-                           !ksh_isalnux(vs->cbuf[ncursor]) &&
-                           !ksh_isspace(vs->cbuf[ncursor]))
+                           !ctype(vs->cbuf[ncursor], C_ALNUX | C_SPACE))
                                ncursor++;
                while (ncursor < vs->linelen &&
-                   ksh_isspace(vs->cbuf[ncursor]))
+                   ctype(vs->cbuf[ncursor], C_SPACE))
                        ncursor++;
        }
        return (ncursor);
@@ -4935,17 +4956,16 @@ backword(int argcnt)
 
        ncursor = vs->cursor;
        while (ncursor > 0 && argcnt--) {
-               while (--ncursor > 0 && ksh_isspace(vs->cbuf[ncursor]))
+               while (--ncursor > 0 && ctype(vs->cbuf[ncursor], C_SPACE))
                        ;
                if (ncursor > 0) {
-                       if (ksh_isalnux(vs->cbuf[ncursor]))
+                       if (ctype(vs->cbuf[ncursor], C_ALNUX))
                                while (--ncursor >= 0 &&
-                                   ksh_isalnux(vs->cbuf[ncursor]))
+                                   ctype(vs->cbuf[ncursor], C_ALNUX))
                                        ;
                        else
                                while (--ncursor >= 0 &&
-                                   !ksh_isalnux(vs->cbuf[ncursor]) &&
-                                   !ksh_isspace(vs->cbuf[ncursor]))
+                                   !ctype(vs->cbuf[ncursor], C_ALNUX | C_SPACE))
                                        ;
                        ncursor++;
                }
@@ -4961,17 +4981,16 @@ endword(int argcnt)
        ncursor = vs->cursor;
        while (ncursor < vs->linelen && argcnt--) {
                while (++ncursor < vs->linelen - 1 &&
-                   ksh_isspace(vs->cbuf[ncursor]))
+                   ctype(vs->cbuf[ncursor], C_SPACE))
                        ;
                if (ncursor < vs->linelen - 1) {
-                       if (ksh_isalnux(vs->cbuf[ncursor]))
+                       if (ctype(vs->cbuf[ncursor], C_ALNUX))
                                while (++ncursor < vs->linelen &&
-                                   ksh_isalnux(vs->cbuf[ncursor]))
+                                   ctype(vs->cbuf[ncursor], C_ALNUX))
                                        ;
                        else
                                while (++ncursor < vs->linelen &&
-                                   !ksh_isalnux(vs->cbuf[ncursor]) &&
-                                   !ksh_isspace(vs->cbuf[ncursor]))
+                                   !ctype(vs->cbuf[ncursor], C_ALNUX | C_SPACE))
                                        ;
                        ncursor--;
                }
@@ -4987,10 +5006,10 @@ Forwword(int argcnt)
        ncursor = vs->cursor;
        while (ncursor < vs->linelen && argcnt--) {
                while (ncursor < vs->linelen &&
-                   !ksh_isspace(vs->cbuf[ncursor]))
+                   !ctype(vs->cbuf[ncursor], C_SPACE))
                        ncursor++;
                while (ncursor < vs->linelen &&
-                   ksh_isspace(vs->cbuf[ncursor]))
+                   ctype(vs->cbuf[ncursor], C_SPACE))
                        ncursor++;
        }
        return (ncursor);
@@ -5003,9 +5022,9 @@ Backword(int argcnt)
 
        ncursor = vs->cursor;
        while (ncursor > 0 && argcnt--) {
-               while (--ncursor >= 0 && ksh_isspace(vs->cbuf[ncursor]))
+               while (--ncursor >= 0 && ctype(vs->cbuf[ncursor], C_SPACE))
                        ;
-               while (ncursor >= 0 && !ksh_isspace(vs->cbuf[ncursor]))
+               while (ncursor >= 0 && !ctype(vs->cbuf[ncursor], C_SPACE))
                        ncursor--;
                ncursor++;
        }
@@ -5020,11 +5039,11 @@ Endword(int argcnt)
        ncursor = vs->cursor;
        while (ncursor < vs->linelen - 1 && argcnt--) {
                while (++ncursor < vs->linelen - 1 &&
-                   ksh_isspace(vs->cbuf[ncursor]))
+                   ctype(vs->cbuf[ncursor], C_SPACE))
                        ;
                if (ncursor < vs->linelen - 1) {
                        while (++ncursor < vs->linelen &&
-                           !ksh_isspace(vs->cbuf[ncursor]))
+                           !ctype(vs->cbuf[ncursor], C_SPACE))
                                ;
                        ncursor--;
                }
@@ -5187,10 +5206,10 @@ display(char *wb1, char *wb2, int leftside)
                                *twb1++ = ' ';
                        } while (++col < winwidth && (col & 7) != 0);
                else if (col < winwidth) {
-                       if (ISCTRL(ch) && /* but not C1 */ ch < 0x80) {
+                       if (ksh_isctrl(ch)) {
                                *twb1++ = '^';
                                if (++col < winwidth) {
-                                       *twb1++ = UNCTRL(ch);
+                                       *twb1++ = ksh_unctrl(ch);
                                        col++;
                                }
                        } else {
@@ -5460,24 +5479,26 @@ print_expansions(struct edstate *est, int cmd MKSH_A_UNUSED)
        redraw_line(false);
        return (0);
 }
+#endif /* !MKSH_S_NOVI */
 
 /* Similar to x_zotc(emacs.c), but no tab weirdness */
 static void
 x_vi_zotc(int c)
 {
-       if (ISCTRL(c)) {
+       if (ksh_isctrl(c)) {
                x_putc('^');
-               c = UNCTRL(c);
+               c = ksh_unctrl(c);
        }
        x_putc(c);
 }
 
+#if !MKSH_S_NOVI
 static void
 vi_error(void)
 {
        /* Beem out of any macros as soon as an error occurs */
        vi_macro_reset();
-       x_putc(7);
+       x_putc(KSH_BEL);
        x_flush();
 }
 
@@ -5602,7 +5623,7 @@ x_eval_region(int c MKSH_A_UNUSED)
        if (cp == NULL) {
                /* command cannot be parsed */
  x_eval_region_err:
-               x_e_putc2(7);
+               x_e_putc2(KSH_BEL);
                x_redraw('\r');
                return (KSTD);
        }