Home | History | Annotate | Download | only in src
      1 /*	$OpenBSD: edit.c,v 1.41 2015/09/01 13:12:31 tedu Exp $	*/
      2 /*	$OpenBSD: edit.h,v 1.9 2011/05/30 17:14:35 martynas Exp $	*/
      3 /*	$OpenBSD: emacs.c,v 1.52 2015/09/10 22:48:58 nicm Exp $	*/
      4 /*	$OpenBSD: vi.c,v 1.30 2015/09/10 22:48:58 nicm Exp $	*/
      5 
      6 /*-
      7  * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
      8  *		 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018
      9  *	mirabilos <m (at) mirbsd.org>
     10  *
     11  * Provided that these terms and disclaimer and all copyright notices
     12  * are retained or reproduced in an accompanying document, permission
     13  * is granted to deal in this work without restriction, including un-
     14  * limited rights to use, publicly perform, distribute, sell, modify,
     15  * merge, give away, or sublicence.
     16  *
     17  * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
     18  * the utmost extent permitted by applicable law, neither express nor
     19  * implied; without malicious intent or gross negligence. In no event
     20  * may a licensor, author or contributor be held liable for indirect,
     21  * direct, other damage, loss, or other issues arising in any way out
     22  * of dealing in the work, even if advised of the possibility of such
     23  * damage or existence of a defect, except proven that it results out
     24  * of said person's immediate fault when using the work as intended.
     25  */
     26 
     27 #include "sh.h"
     28 
     29 #ifndef MKSH_NO_CMDLINE_EDITING
     30 
     31 __RCSID("$MirOS: src/bin/mksh/edit.c,v 1.342 2018/01/14 00:03:00 tg Exp $");
     32 
     33 /*
     34  * in later versions we might use libtermcap for this, but since external
     35  * dependencies are problematic, this has not yet been decided on; another
     36  * good string is KSH_ESC_STRING "c" except on hardware terminals like the
     37  * DEC VT420 which do a full power cycle then...
     38  */
     39 #ifndef MKSH_CLS_STRING
     40 #define MKSH_CLS_STRING		KSH_ESC_STRING "[;H" KSH_ESC_STRING "[J"
     41 #endif
     42 
     43 /* tty driver characters we are interested in */
     44 #define EDCHAR_DISABLED	0xFFFFU
     45 #define EDCHAR_INITIAL	0xFFFEU
     46 static struct {
     47 	unsigned short erase;
     48 	unsigned short kill;
     49 	unsigned short werase;
     50 	unsigned short intr;
     51 	unsigned short quit;
     52 	unsigned short eof;
     53 } edchars;
     54 
     55 #define isched(x,e) ((unsigned short)(unsigned char)(x) == (e))
     56 #define isedchar(x) (!((x) & ~0xFF))
     57 #ifndef _POSIX_VDISABLE
     58 #define toedchar(x) ((unsigned short)(unsigned char)(x))
     59 #else
     60 #define toedchar(x) (((_POSIX_VDISABLE != -1) && ((x) == _POSIX_VDISABLE)) ? \
     61 			((unsigned short)EDCHAR_DISABLED) : \
     62 			((unsigned short)(unsigned char)(x)))
     63 #endif
     64 
     65 /* x_cf_glob() flags */
     66 #define XCF_COMMAND	BIT(0)	/* Do command completion */
     67 #define XCF_FILE	BIT(1)	/* Do file completion */
     68 #define XCF_FULLPATH	BIT(2)	/* command completion: store full path */
     69 #define XCF_COMMAND_FILE (XCF_COMMAND | XCF_FILE)
     70 #define XCF_IS_COMMAND	BIT(3)	/* return flag: is command */
     71 #define XCF_IS_NOSPACE	BIT(4)	/* return flag: do not append a space */
     72 
     73 static char editmode;
     74 static int xx_cols;			/* for Emacs mode */
     75 static int modified;			/* buffer has been "modified" */
     76 static char *holdbufp;			/* place to hold last edit buffer */
     77 
     78 /* 0=dumb 1=tmux (for now) */
     79 static uint8_t x_term_mode;
     80 
     81 static void x_adjust(void);
     82 static int x_getc(void);
     83 static void x_putcf(int);
     84 static void x_modified(void);
     85 static void x_mode(bool);
     86 static int x_do_comment(char *, ssize_t, ssize_t *);
     87 static void x_print_expansions(int, char * const *, bool);
     88 static int x_cf_glob(int *, const char *, int, int, int *, int *, char ***);
     89 static size_t x_longest_prefix(int, char * const *);
     90 static void x_glob_hlp_add_qchar(char *);
     91 static char *x_glob_hlp_tilde_and_rem_qchar(char *, bool);
     92 static size_t x_basename(const char *, const char *);
     93 static void x_free_words(int, char **);
     94 static int x_escape(const char *, size_t, int (*)(const char *, size_t));
     95 static int x_emacs(char *);
     96 static void x_init_prompt(bool);
     97 #if !MKSH_S_NOVI
     98 static int x_vi(char *);
     99 #endif
    100 static void x_intr(int, int) MKSH_A_NORETURN;
    101 
    102 #define x_flush()	shf_flush(shl_out)
    103 #if defined(MKSH_SMALL) && !defined(MKSH_SMALL_BUT_FAST)
    104 #define x_putc(c)	x_putcf(c)
    105 #else
    106 #define x_putc(c)	shf_putc((c), shl_out)
    107 #endif
    108 
    109 static int path_order_cmp(const void *, const void *);
    110 static void glob_table(const char *, XPtrV *, struct table *);
    111 static void glob_path(int, const char *, XPtrV *, const char *);
    112 static int x_file_glob(int *, char *, char ***);
    113 static int x_command_glob(int, char *, char ***);
    114 static int x_locate_word(const char *, int, int, int *, bool *);
    115 
    116 static int x_e_getmbc(char *);
    117 
    118 /* +++ generic editing functions +++ */
    119 
    120 /*
    121  * read an edited command line
    122  */
    123 int
    124 x_read(char *buf)
    125 {
    126 	int i;
    127 
    128 	x_mode(true);
    129 	modified = 1;
    130 	if (Flag(FEMACS) || Flag(FGMACS))
    131 		i = x_emacs(buf);
    132 #if !MKSH_S_NOVI
    133 	else if (Flag(FVI))
    134 		i = x_vi(buf);
    135 #endif
    136 	else
    137 		/* internal error */
    138 		i = -1;
    139 	editmode = 0;
    140 	x_mode(false);
    141 	return (i);
    142 }
    143 
    144 /* tty I/O */
    145 
    146 static int
    147 x_getc(void)
    148 {
    149 #ifdef __OS2__
    150 	return (_read_kbd(0, 1, 0));
    151 #else
    152 	char c;
    153 	ssize_t n;
    154 
    155 	while ((n = blocking_read(STDIN_FILENO, &c, 1)) < 0 && errno == EINTR)
    156 		if (trap) {
    157 			x_mode(false);
    158 			runtraps(0);
    159 #ifdef SIGWINCH
    160 			if (got_winch) {
    161 				change_winsz();
    162 				if (x_cols != xx_cols && editmode == 1) {
    163 					/* redraw line in Emacs mode */
    164 					xx_cols = x_cols;
    165 					x_init_prompt(false);
    166 					x_adjust();
    167 				}
    168 			}
    169 #endif
    170 			x_mode(true);
    171 		}
    172 	return ((n == 1) ? (int)(unsigned char)c : -1);
    173 #endif
    174 }
    175 
    176 static void
    177 x_putcf(int c)
    178 {
    179 	shf_putc(c, shl_out);
    180 }
    181 
    182 /*********************************
    183  * Misc common code for vi/emacs *
    184  *********************************/
    185 
    186 /*-
    187  * Handle the commenting/uncommenting of a line.
    188  * Returns:
    189  *	1 if a carriage return is indicated (comment added)
    190  *	0 if no return (comment removed)
    191  *	-1 if there is an error (not enough room for comment chars)
    192  * If successful, *lenp contains the new length. Note: cursor should be
    193  * moved to the start of the line after (un)commenting.
    194  */
    195 static int
    196 x_do_comment(char *buf, ssize_t bsize, ssize_t *lenp)
    197 {
    198 	ssize_t i, j, len = *lenp;
    199 
    200 	if (len == 0)
    201 		/* somewhat arbitrary - it's what AT&T ksh does */
    202 		return (1);
    203 
    204 	/* Already commented? */
    205 	if (buf[0] == '#') {
    206 		bool saw_nl = false;
    207 
    208 		for (j = 0, i = 1; i < len; i++) {
    209 			if (!saw_nl || buf[i] != '#')
    210 				buf[j++] = buf[i];
    211 			saw_nl = buf[i] == '\n';
    212 		}
    213 		*lenp = j;
    214 		return (0);
    215 	} else {
    216 		int n = 1;
    217 
    218 		/* See if there's room for the #s - 1 per \n */
    219 		for (i = 0; i < len; i++)
    220 			if (buf[i] == '\n')
    221 				n++;
    222 		if (len + n >= bsize)
    223 			return (-1);
    224 		/* Now add them... */
    225 		for (i = len, j = len + n; --i >= 0; ) {
    226 			if (buf[i] == '\n')
    227 				buf[--j] = '#';
    228 			buf[--j] = buf[i];
    229 		}
    230 		buf[0] = '#';
    231 		*lenp += n;
    232 		return (1);
    233 	}
    234 }
    235 
    236 /****************************************************
    237  * Common file/command completion code for vi/emacs *
    238  ****************************************************/
    239 
    240 static void
    241 x_print_expansions(int nwords, char * const *words, bool is_command)
    242 {
    243 	bool use_copy = false;
    244 	size_t prefix_len;
    245 	XPtrV l = { NULL, 0, 0 };
    246 	struct columnise_opts co;
    247 
    248 	/*
    249 	 * Check if all matches are in the same directory (in this
    250 	 * case, we want to omit the directory name)
    251 	 */
    252 	if (!is_command &&
    253 	    (prefix_len = x_longest_prefix(nwords, words)) > 0) {
    254 		int i;
    255 
    256 		/* Special case for 1 match (prefix is whole word) */
    257 		if (nwords == 1)
    258 			prefix_len = x_basename(words[0], NULL);
    259 		/* Any (non-trailing) slashes in non-common word suffixes? */
    260 		for (i = 0; i < nwords; i++)
    261 			if (x_basename(words[i] + prefix_len, NULL) >
    262 			    prefix_len)
    263 				break;
    264 		/* All in same directory? */
    265 		if (i == nwords) {
    266 			while (prefix_len > 0 &&
    267 			    !mksh_cdirsep(words[0][prefix_len - 1]))
    268 				prefix_len--;
    269 			use_copy = true;
    270 			XPinit(l, nwords + 1);
    271 			for (i = 0; i < nwords; i++)
    272 				XPput(l, words[i] + prefix_len);
    273 			XPput(l, NULL);
    274 		}
    275 	}
    276 	/*
    277 	 * Enumerate expansions
    278 	 */
    279 	x_putc('\r');
    280 	x_putc('\n');
    281 	co.shf = shl_out;
    282 	co.linesep = '\n';
    283 	co.do_last = true;
    284 	co.prefcol = false;
    285 	pr_list(&co, use_copy ? (char **)XPptrv(l) : words);
    286 
    287 	if (use_copy)
    288 		/* not x_free_words() */
    289 		XPfree(l);
    290 }
    291 
    292 /*
    293  * Convert backslash-escaped string to QCHAR-escaped
    294  * string useful for globbing; loses QCHAR unless it
    295  * can squeeze in, eg. by previous loss of backslash
    296  */
    297 static void
    298 x_glob_hlp_add_qchar(char *cp)
    299 {
    300 	char ch, *dp = cp;
    301 	bool escaping = false;
    302 
    303 	while ((ch = *cp++)) {
    304 		if (ch == '\\' && !escaping) {
    305 			escaping = true;
    306 			continue;
    307 		}
    308 		if (escaping || (ch == QCHAR && (cp - dp) > 1)) {
    309 			/*
    310 			 * empirically made list of chars to escape
    311 			 * for globbing as well as QCHAR itself
    312 			 */
    313 			switch (ord(ch)) {
    314 			case QCHAR:
    315 			case ORD('$'):
    316 			case ORD('*'):
    317 			case ORD('?'):
    318 			case ORD('['):
    319 			case ORD('\\'):
    320 			case ORD('`'):
    321 				*dp++ = QCHAR;
    322 				break;
    323 			}
    324 			escaping = false;
    325 		}
    326 		*dp++ = ch;
    327 	}
    328 	*dp = '\0';
    329 }
    330 
    331 /*
    332  * Run tilde expansion on argument string, return the result
    333  * after unescaping; if the flag is set, the original string
    334  * is freed if changed and assumed backslash-escaped, if not
    335  * it is assumed QCHAR-escaped
    336  */
    337 static char *
    338 x_glob_hlp_tilde_and_rem_qchar(char *s, bool magic_flag)
    339 {
    340 	char ch, *cp, *dp;
    341 
    342 	/*
    343 	 * On the string, check whether we have a tilde expansion,
    344 	 * and if so, discern "~foo/bar" and "~/baz" from "~blah";
    345 	 * if we have a directory part (the former), try to expand
    346 	 */
    347 	if (*s == '~' && (cp = /* not sdirsep */ strchr(s, '/')) != NULL) {
    348 		/* ok, so split into "~foo"/"bar" or "~"/"baz" */
    349 		*cp++ = 0;
    350 		/* try to expand the tilde */
    351 		if (!(dp = do_tilde(s + 1))) {
    352 			/* nope, revert damage */
    353 			*--cp = '/';
    354 		} else {
    355 			/* ok, expand and replace */
    356 			cp = shf_smprintf(Tf_sSs, dp, cp);
    357 			if (magic_flag)
    358 				afree(s, ATEMP);
    359 			s = cp;
    360 		}
    361 	}
    362 
    363 	/* ... convert it from backslash-escaped via QCHAR-escaped... */
    364 	if (magic_flag)
    365 		x_glob_hlp_add_qchar(s);
    366 	/* ... to unescaped, for comparison with the matches */
    367 	cp = dp = s;
    368 
    369 	while ((ch = *cp++)) {
    370 		if (ch == QCHAR && !(ch = *cp++))
    371 			break;
    372 		*dp++ = ch;
    373 	}
    374 	*dp = '\0';
    375 
    376 	return (s);
    377 }
    378 
    379 /**
    380  * Do file globbing:
    381  *	- does expansion, checks for no match, etc.
    382  *	- sets *wordsp to array of matching strings
    383  *	- returns number of matching strings
    384  */
    385 static int
    386 x_file_glob(int *flagsp, char *toglob, char ***wordsp)
    387 {
    388 	char **words, *cp;
    389 	int nwords;
    390 	XPtrV w;
    391 	struct source *s, *sold;
    392 
    393 	/* remove all escaping backward slashes */
    394 	x_glob_hlp_add_qchar(toglob);
    395 
    396 	/*
    397 	 * Convert "foo*" (toglob) to an array of strings (words)
    398 	 */
    399 	sold = source;
    400 	s = pushs(SWSTR, ATEMP);
    401 	s->start = s->str = toglob;
    402 	source = s;
    403 	if (yylex(ONEWORD | LQCHAR) != LWORD) {
    404 		source = sold;
    405 		internal_warningf(Tfg_badsubst);
    406 		return (0);
    407 	}
    408 	source = sold;
    409 	afree(s, ATEMP);
    410 	XPinit(w, 32);
    411 	cp = yylval.cp;
    412 	while (*cp == CHAR || *cp == QCHAR)
    413 		cp += 2;
    414 	nwords = DOGLOB | DOTILDE | DOMARKDIRS;
    415 	if (*cp != EOS) {
    416 		/* probably a $FOO expansion */
    417 		*flagsp |= XCF_IS_NOSPACE;
    418 		/* this always results in at most one match */
    419 		nwords = 0;
    420 	}
    421 	expand(yylval.cp, &w, nwords);
    422 	XPput(w, NULL);
    423 	words = (char **)XPclose(w);
    424 
    425 	for (nwords = 0; words[nwords]; nwords++)
    426 		;
    427 	if (nwords == 1) {
    428 		struct stat statb;
    429 
    430 		/* Expand any tilde and drop all QCHAR for comparison */
    431 		toglob = x_glob_hlp_tilde_and_rem_qchar(toglob, false);
    432 
    433 		/*
    434 		 * Check if globbing failed (returned glob pattern),
    435 		 * but be careful (e.g. toglob == "ab*" when the file
    436 		 * "ab*" exists is not an error).
    437 		 * Also, check for empty result - happens if we tried
    438 		 * to glob something which evaluated to an empty
    439 		 * string (e.g., "$FOO" when there is no FOO, etc).
    440 		 */
    441 		if ((strcmp(words[0], toglob) == 0 &&
    442 		    stat(words[0], &statb) < 0) ||
    443 		    words[0][0] == '\0') {
    444 			x_free_words(nwords, words);
    445 			words = NULL;
    446 			nwords = 0;
    447 		}
    448 	}
    449 
    450 	if ((*wordsp = nwords ? words : NULL) == NULL && words != NULL)
    451 		x_free_words(nwords, words);
    452 
    453 	return (nwords);
    454 }
    455 
    456 /* Data structure used in x_command_glob() */
    457 struct path_order_info {
    458 	char *word;
    459 	size_t base;
    460 	size_t path_order;
    461 };
    462 
    463 /* Compare routine used in x_command_glob() */
    464 static int
    465 path_order_cmp(const void *aa, const void *bb)
    466 {
    467 	const struct path_order_info *a = (const struct path_order_info *)aa;
    468 	const struct path_order_info *b = (const struct path_order_info *)bb;
    469 	int t;
    470 
    471 	if ((t = ascstrcmp(a->word + a->base, b->word + b->base)))
    472 		return (t);
    473 	if (a->path_order > b->path_order)
    474 		return (1);
    475 	if (a->path_order < b->path_order)
    476 		return (-1);
    477 	return (0);
    478 }
    479 
    480 static int
    481 x_command_glob(int flags, char *toglob, char ***wordsp)
    482 {
    483 	char *pat, *fpath;
    484 	size_t nwords;
    485 	XPtrV w;
    486 	struct block *l;
    487 
    488 	/* Convert "foo*" (toglob) to a pattern for future use */
    489 	pat = evalstr(toglob, DOPAT | DOTILDE);
    490 
    491 	XPinit(w, 32);
    492 
    493 	glob_table(pat, &w, &keywords);
    494 	glob_table(pat, &w, &aliases);
    495 	glob_table(pat, &w, &builtins);
    496 	for (l = e->loc; l; l = l->next)
    497 		glob_table(pat, &w, &l->funs);
    498 
    499 	glob_path(flags, pat, &w, path);
    500 	if ((fpath = str_val(global(TFPATH))) != null)
    501 		glob_path(flags, pat, &w, fpath);
    502 
    503 	nwords = XPsize(w);
    504 
    505 	if (!nwords) {
    506 		*wordsp = NULL;
    507 		XPfree(w);
    508 		return (0);
    509 	}
    510 	/* Sort entries */
    511 	if (flags & XCF_FULLPATH) {
    512 		/* Sort by basename, then path order */
    513 		struct path_order_info *info, *last_info = NULL;
    514 		char **words = (char **)XPptrv(w);
    515 		size_t i, path_order = 0;
    516 
    517 		info = (struct path_order_info *)
    518 		    alloc2(nwords, sizeof(struct path_order_info), ATEMP);
    519 		for (i = 0; i < nwords; i++) {
    520 			info[i].word = words[i];
    521 			info[i].base = x_basename(words[i], NULL);
    522 			if (!last_info || info[i].base != last_info->base ||
    523 			    strncmp(words[i], last_info->word, info[i].base) != 0) {
    524 				last_info = &info[i];
    525 				path_order++;
    526 			}
    527 			info[i].path_order = path_order;
    528 		}
    529 		qsort(info, nwords, sizeof(struct path_order_info),
    530 		    path_order_cmp);
    531 		for (i = 0; i < nwords; i++)
    532 			words[i] = info[i].word;
    533 		afree(info, ATEMP);
    534 	} else {
    535 		/* Sort and remove duplicate entries */
    536 		char **words = (char **)XPptrv(w);
    537 		size_t i, j;
    538 
    539 		qsort(words, nwords, sizeof(void *), ascpstrcmp);
    540 		for (i = j = 0; i < nwords - 1; i++) {
    541 			if (strcmp(words[i], words[i + 1]))
    542 				words[j++] = words[i];
    543 			else
    544 				afree(words[i], ATEMP);
    545 		}
    546 		words[j++] = words[i];
    547 		w.len = nwords = j;
    548 	}
    549 
    550 	XPput(w, NULL);
    551 	*wordsp = (char **)XPclose(w);
    552 
    553 	return (nwords);
    554 }
    555 
    556 #define IS_WORDC(c)	(!ctype(c, C_EDNWC))
    557 
    558 static int
    559 x_locate_word(const char *buf, int buflen, int pos, int *startp,
    560     bool *is_commandp)
    561 {
    562 	int start, end;
    563 
    564 	/* Bad call? Probably should report error */
    565 	if (pos < 0 || pos > buflen) {
    566 		*startp = pos;
    567 		*is_commandp = false;
    568 		return (0);
    569 	}
    570 	/* The case where pos == buflen happens to take care of itself... */
    571 
    572 	start = pos;
    573 	/*
    574 	 * Keep going backwards to start of word (has effect of allowing
    575 	 * one blank after the end of a word)
    576 	 */
    577 	for (; (start > 0 && IS_WORDC(buf[start - 1])) ||
    578 	    (start > 1 && buf[start - 2] == '\\'); start--)
    579 		;
    580 	/* Go forwards to end of word */
    581 	for (end = start; end < buflen && IS_WORDC(buf[end]); end++) {
    582 		if (buf[end] == '\\' && (end + 1) < buflen)
    583 			end++;
    584 	}
    585 
    586 	if (is_commandp) {
    587 		bool iscmd;
    588 		int p = start - 1;
    589 
    590 		/* Figure out if this is a command */
    591 		while (p >= 0 && ctype(buf[p], C_SPACE))
    592 			p--;
    593 		iscmd = p < 0 || ctype(buf[p], C_EDCMD);
    594 		if (iscmd) {
    595 			/*
    596 			 * If command has a /, path, etc. is not searched;
    597 			 * only current directory is searched which is just
    598 			 * like file globbing.
    599 			 */
    600 			for (p = start; p < end; p++)
    601 				if (mksh_cdirsep(buf[p]))
    602 					break;
    603 			iscmd = p == end;
    604 		}
    605 		*is_commandp = iscmd;
    606 	}
    607 	*startp = start;
    608 
    609 	return (end - start);
    610 }
    611 
    612 static int
    613 x_cf_glob(int *flagsp, const char *buf, int buflen, int pos, int *startp,
    614     int *endp, char ***wordsp)
    615 {
    616 	int len, nwords = 0;
    617 	char **words = NULL;
    618 	bool is_command;
    619 
    620 	len = x_locate_word(buf, buflen, pos, startp, &is_command);
    621 	if (!((*flagsp) & XCF_COMMAND))
    622 		is_command = false;
    623 	/*
    624 	 * Don't do command globing on zero length strings - it takes too
    625 	 * long and isn't very useful. File globs are more likely to be
    626 	 * useful, so allow these.
    627 	 */
    628 	if (len == 0 && is_command)
    629 		return (0);
    630 
    631 	if (len >= 0) {
    632 		char *toglob, *s;
    633 
    634 		/*
    635 		 * Given a string, copy it and possibly add a '*' to the end.
    636 		 */
    637 
    638 		strndupx(toglob, buf + *startp, len + /* the '*' */ 1, ATEMP);
    639 		toglob[len] = '\0';
    640 
    641 		/*
    642 		 * If the pathname contains a wildcard (an unquoted '*',
    643 		 * '?', or '[') or an extglob, then it is globbed based
    644 		 * on that value (i.e., without the appended '*'). Same
    645 		 * for parameter substitutions (as in cat $HOME/.ss)
    646 		 * without appending a trailing space (LP: #710539), as
    647 		 * well as for ~foo (but not ~foo/).
    648 		 */
    649 		for (s = toglob; *s; s++) {
    650 			if (*s == '\\' && s[1])
    651 				s++;
    652 			else if (ctype(*s, C_QUEST | C_DOLAR) ||
    653 			    ord(*s) == ORD('*') || ord(*s) == ORD('[') ||
    654 			    /* ?() *() +() @() !() but two already checked */
    655 			    (ord(s[1]) == ORD('(' /*)*/) &&
    656 			    (ord(*s) == ORD('+') || ord(*s) == ORD('@') ||
    657 			    ord(*s) == ORD('!')))) {
    658 				/*
    659 				 * just expand based on the extglob
    660 				 * or parameter
    661 				 */
    662 				goto dont_add_glob;
    663 			}
    664 		}
    665 
    666 		if (*toglob == '~' && /* not vdirsep */ !vstrchr(toglob, '/')) {
    667 			/* neither for '~foo' (but '~foo/bar') */
    668 			*flagsp |= XCF_IS_NOSPACE;
    669 			goto dont_add_glob;
    670 		}
    671 
    672 		/* append a glob */
    673 		toglob[len] = '*';
    674 		toglob[len + 1] = '\0';
    675  dont_add_glob:
    676 		/*
    677 		 * Expand (glob) it now.
    678 		 */
    679 
    680 		nwords = is_command ?
    681 		    x_command_glob(*flagsp, toglob, &words) :
    682 		    x_file_glob(flagsp, toglob, &words);
    683 		afree(toglob, ATEMP);
    684 	}
    685 	if (nwords == 0) {
    686 		*wordsp = NULL;
    687 		return (0);
    688 	}
    689 	if (is_command)
    690 		*flagsp |= XCF_IS_COMMAND;
    691 	*wordsp = words;
    692 	*endp = *startp + len;
    693 
    694 	return (nwords);
    695 }
    696 
    697 /*
    698  * Find longest common prefix
    699  */
    700 static size_t
    701 x_longest_prefix(int nwords, char * const * words)
    702 {
    703 	int i;
    704 	size_t j, prefix_len;
    705 	char *p;
    706 
    707 	if (nwords <= 0)
    708 		return (0);
    709 
    710 	prefix_len = strlen(words[0]);
    711 	for (i = 1; i < nwords; i++)
    712 		for (j = 0, p = words[i]; j < prefix_len; j++)
    713 			if (p[j] != words[0][j]) {
    714 				prefix_len = j;
    715 				break;
    716 			}
    717 	/* false for nwords==1 as 0 = words[0][prefix_len] then */
    718 	if (UTFMODE && prefix_len && (rtt2asc(words[0][prefix_len]) & 0xC0) == 0x80)
    719 		while (prefix_len && (rtt2asc(words[0][prefix_len]) & 0xC0) != 0xC0)
    720 			--prefix_len;
    721 	return (prefix_len);
    722 }
    723 
    724 static void
    725 x_free_words(int nwords, char **words)
    726 {
    727 	while (nwords)
    728 		afree(words[--nwords], ATEMP);
    729 	afree(words, ATEMP);
    730 }
    731 
    732 /*-
    733  * Return the offset of the basename of string s (which ends at se - need not
    734  * be null terminated). Trailing slashes are ignored. If s is just a slash,
    735  * then the offset is 0 (actually, length - 1).
    736  *	s		Return
    737  *	/etc		1
    738  *	/etc/		1
    739  *	/etc//		1
    740  *	/etc/fo		5
    741  *	foo		0
    742  *	///		2
    743  *			0
    744  */
    745 static size_t
    746 x_basename(const char *s, const char *se)
    747 {
    748 	const char *p;
    749 
    750 	if (se == NULL)
    751 		se = strnul(s);
    752 	if (s == se)
    753 		return (0);
    754 
    755 	/* skip trailing directory separators */
    756 	p = se - 1;
    757 	while (p > s && mksh_cdirsep(*p))
    758 		--p;
    759 	/* drop last component */
    760 	while (p > s && !mksh_cdirsep(*p))
    761 		--p;
    762 	if (mksh_cdirsep(*p) && p + 1 < se)
    763 		++p;
    764 
    765 	return (p - s);
    766 }
    767 
    768 /*
    769  * Apply pattern matching to a table: all table entries that match a pattern
    770  * are added to wp.
    771  */
    772 static void
    773 glob_table(const char *pat, XPtrV *wp, struct table *tp)
    774 {
    775 	struct tstate ts;
    776 	struct tbl *te;
    777 
    778 	ktwalk(&ts, tp);
    779 	while ((te = ktnext(&ts)))
    780 		if (gmatchx(te->name, pat, false)) {
    781 			char *cp;
    782 
    783 			strdupx(cp, te->name, ATEMP);
    784 			XPput(*wp, cp);
    785 		}
    786 }
    787 
    788 static void
    789 glob_path(int flags, const char *pat, XPtrV *wp, const char *lpath)
    790 {
    791 	const char *sp = lpath, *p;
    792 	char *xp, **words;
    793 	size_t pathlen, patlen, oldsize, newsize, i, j;
    794 	XString xs;
    795 
    796 	patlen = strlen(pat);
    797 	checkoktoadd(patlen, 129 + X_EXTRA);
    798 	++patlen;
    799 	Xinit(xs, xp, patlen + 128, ATEMP);
    800 	while (sp) {
    801 		xp = Xstring(xs, xp);
    802 		if (!(p = cstrchr(sp, MKSH_PATHSEPC)))
    803 			p = strnul(sp);
    804 		pathlen = p - sp;
    805 		if (pathlen) {
    806 			/*
    807 			 * Copy sp into xp, stuffing any MAGIC characters
    808 			 * on the way
    809 			 */
    810 			const char *s = sp;
    811 
    812 			XcheckN(xs, xp, pathlen * 2);
    813 			while (s < p) {
    814 				if (ISMAGIC(*s))
    815 					*xp++ = MAGIC;
    816 				*xp++ = *s++;
    817 			}
    818 			*xp++ = '/';
    819 			pathlen++;
    820 		}
    821 		sp = p;
    822 		XcheckN(xs, xp, patlen);
    823 		memcpy(xp, pat, patlen);
    824 
    825 		oldsize = XPsize(*wp);
    826 		/* mark dirs */
    827 		glob_str(Xstring(xs, xp), wp, true);
    828 		newsize = XPsize(*wp);
    829 
    830 		/* Check that each match is executable... */
    831 		words = (char **)XPptrv(*wp);
    832 		for (i = j = oldsize; i < newsize; i++) {
    833 			if (ksh_access(words[i], X_OK) == 0) {
    834 				words[j] = words[i];
    835 				if (!(flags & XCF_FULLPATH))
    836 					memmove(words[j], words[j] + pathlen,
    837 					    strlen(words[j] + pathlen) + 1);
    838 				j++;
    839 			} else
    840 				afree(words[i], ATEMP);
    841 		}
    842 		wp->len = j;
    843 
    844 		if (!*sp++)
    845 			break;
    846 	}
    847 	Xfree(xs, xp);
    848 }
    849 
    850 /*
    851  * if argument string contains any special characters, they will
    852  * be escaped and the result will be put into edit buffer by
    853  * keybinding-specific function
    854  */
    855 static int
    856 x_escape(const char *s, size_t len, int (*putbuf_func)(const char *, size_t))
    857 {
    858 	size_t add = 0, wlen = len;
    859 	int rval = 0;
    860 
    861 	while (wlen - add > 0)
    862 		if (ctype(s[add], C_IFS | C_EDQ)) {
    863 			if (putbuf_func(s, add) != 0) {
    864 				rval = -1;
    865 				break;
    866 			}
    867 			putbuf_func(s[add] == '\n' ? "'" : "\\", 1);
    868 			putbuf_func(&s[add], 1);
    869 			if (s[add] == '\n')
    870 				putbuf_func("'", 1);
    871 
    872 			add++;
    873 			wlen -= add;
    874 			s += add;
    875 			add = 0;
    876 		} else
    877 			++add;
    878 	if (wlen > 0 && rval == 0)
    879 		rval = putbuf_func(s, wlen);
    880 
    881 	return (rval);
    882 }
    883 
    884 
    885 /* +++ emacs editing mode +++ */
    886 
    887 static	Area	aedit;
    888 #define	AEDIT	&aedit		/* area for kill ring and macro defns */
    889 
    890 /* values returned by keyboard functions */
    891 #define	KSTD	0
    892 #define	KEOL	1		/* ^M, ^J */
    893 #define	KINTR	2		/* ^G, ^C */
    894 
    895 struct x_ftab {
    896 	int (*xf_func)(int c);
    897 	const char *xf_name;
    898 	short xf_flags;
    899 };
    900 
    901 struct x_defbindings {
    902 	unsigned char xdb_func;	/* XFUNC_* */
    903 	unsigned char xdb_tab;
    904 	unsigned char xdb_char;
    905 };
    906 
    907 #define XF_ARG		1	/* command takes number prefix */
    908 #define	XF_NOBIND	2	/* not allowed to bind to function */
    909 #define	XF_PREFIX	4	/* function sets prefix */
    910 
    911 #define X_NTABS		4			/* normal, meta1, meta2, pc */
    912 #define X_TABSZ		256			/* size of keydef tables etc */
    913 
    914 /*-
    915  * Arguments for do_complete()
    916  * 0 = enumerate	M-=	complete as much as possible and then list
    917  * 1 = complete		M-Esc
    918  * 2 = list		M-?
    919  */
    920 typedef enum {
    921 	CT_LIST,	/* list the possible completions */
    922 	CT_COMPLETE,	/* complete to longest prefix */
    923 	CT_COMPLIST	/* complete and then list (if non-exact) */
    924 } Comp_type;
    925 
    926 /*
    927  * The following are used for my horizontal scrolling stuff
    928  */
    929 static char *xbuf;		/* beg input buffer */
    930 static char *xend;		/* end input buffer */
    931 static char *xcp;		/* current position */
    932 static char *xep;		/* current end */
    933 static char *xbp;		/* start of visible portion of input buffer */
    934 static char *xlp;		/* last char visible on screen */
    935 static bool x_adj_ok;
    936 /*
    937  * we use x_adj_done so that functions can tell
    938  * whether x_adjust() has been called while they are active.
    939  */
    940 static int x_adj_done;		/* is incremented by x_adjust() */
    941 
    942 static int x_displen;
    943 static int x_arg;		/* general purpose arg */
    944 static bool x_arg_defaulted;	/* x_arg not explicitly set; defaulted to 1 */
    945 
    946 static bool xlp_valid;		/* lastvis pointer was recalculated */
    947 
    948 static char **x_histp;		/* history position */
    949 static int x_nextcmd;		/* for newline-and-next */
    950 static char **x_histncp;	/* saved x_histp for " */
    951 static char **x_histmcp;	/* saved x_histp for " */
    952 static char *xmp;		/* mark pointer */
    953 static unsigned char x_last_command;
    954 static unsigned char (*x_tab)[X_TABSZ];	/* key definition */
    955 #ifndef MKSH_SMALL
    956 static char *(*x_atab)[X_TABSZ];	/* macro definitions */
    957 #endif
    958 static unsigned char x_bound[(X_TABSZ * X_NTABS + 7) / 8];
    959 #define KILLSIZE	20
    960 static char *killstack[KILLSIZE];
    961 static int killsp, killtp;
    962 static int x_curprefix;
    963 #ifndef MKSH_SMALL
    964 static char *macroptr;		/* bind key macro active? */
    965 #endif
    966 #if !MKSH_S_NOVI
    967 static int winwidth;		/* width of window */
    968 static char *wbuf[2];		/* window buffers */
    969 static int wbuf_len;		/* length of window buffers (x_cols - 3) */
    970 static int win;			/* window buffer in use */
    971 static char morec;		/* more character at right of window */
    972 static int lastref;		/* argument to last refresh() */
    973 static int holdlen;		/* length of holdbuf */
    974 #endif
    975 static int pwidth;		/* width of prompt */
    976 static int prompt_trunc;	/* how much of prompt to truncate or -1 */
    977 static int x_col;		/* current column on line */
    978 
    979 static int x_ins(const char *);
    980 static void x_delete(size_t, bool);
    981 static size_t x_bword(void);
    982 static size_t x_fword(bool);
    983 static void x_goto(char *);
    984 static char *x_bs0(char *, char *) MKSH_A_PURE;
    985 static void x_bs3(char **);
    986 static int x_size2(char *, char **);
    987 static void x_zots(char *);
    988 static void x_zotc3(char **);
    989 static void x_vi_zotc(int);
    990 static void x_load_hist(char **);
    991 static int x_search(char *, int, int);
    992 #ifndef MKSH_SMALL
    993 static int x_search_dir(int);
    994 #endif
    995 static int x_match(char *, char *);
    996 static void x_redraw(int);
    997 static void x_push(size_t);
    998 static char *x_mapin(const char *, Area *);
    999 static char *x_mapout(int);
   1000 static void x_mapout2(int, char **);
   1001 static void x_print(int, int);
   1002 static void x_e_ungetc(int);
   1003 static int x_e_getc(void);
   1004 static void x_e_putc2(int);
   1005 static void x_e_putc3(const char **);
   1006 static void x_e_puts(const char *);
   1007 #ifndef MKSH_SMALL
   1008 static int x_fold_case(int);
   1009 #endif
   1010 static char *x_lastcp(void);
   1011 static void x_lastpos(void);
   1012 static void do_complete(int, Comp_type);
   1013 static size_t x_nb2nc(size_t) MKSH_A_PURE;
   1014 
   1015 static int unget_char = -1;
   1016 
   1017 static int x_do_ins(const char *, size_t);
   1018 static void bind_if_not_bound(int, int, int);
   1019 
   1020 enum emacs_funcs {
   1021 #define EMACSFN_ENUMS
   1022 #include "emacsfn.h"
   1023 	XFUNC_MAX
   1024 };
   1025 
   1026 #define EMACSFN_DEFNS
   1027 #include "emacsfn.h"
   1028 
   1029 static const struct x_ftab x_ftab[] = {
   1030 #define EMACSFN_ITEMS
   1031 #include "emacsfn.h"
   1032 };
   1033 
   1034 static struct x_defbindings const x_defbindings[] = {
   1035 	{ XFUNC_del_back,		0,  CTRL_QM	},
   1036 	{ XFUNC_del_bword,		1,  CTRL_QM	},
   1037 	{ XFUNC_eot_del,		0,  CTRL_D	},
   1038 	{ XFUNC_del_back,		0,  CTRL_H	},
   1039 	{ XFUNC_del_bword,		1,  CTRL_H	},
   1040 	{ XFUNC_del_bword,		1,	'h'	},
   1041 	{ XFUNC_mv_bword,		1,	'b'	},
   1042 	{ XFUNC_mv_fword,		1,	'f'	},
   1043 	{ XFUNC_del_fword,		1,	'd'	},
   1044 	{ XFUNC_mv_back,		0,  CTRL_B	},
   1045 	{ XFUNC_mv_forw,		0,  CTRL_F	},
   1046 	{ XFUNC_search_char_forw,	0,  CTRL_BC	},
   1047 	{ XFUNC_search_char_back,	1,  CTRL_BC	},
   1048 	{ XFUNC_newline,		0,  CTRL_M	},
   1049 	{ XFUNC_newline,		0,  CTRL_J	},
   1050 	{ XFUNC_end_of_text,		0,  CTRL_US	},
   1051 	{ XFUNC_abort,			0,  CTRL_G	},
   1052 	{ XFUNC_prev_com,		0,  CTRL_P	},
   1053 	{ XFUNC_next_com,		0,  CTRL_N	},
   1054 	{ XFUNC_nl_next_com,		0,  CTRL_O	},
   1055 	{ XFUNC_search_hist,		0,  CTRL_R	},
   1056 	{ XFUNC_beg_hist,		1,	'<'	},
   1057 	{ XFUNC_end_hist,		1,	'>'	},
   1058 	{ XFUNC_goto_hist,		1,	'g'	},
   1059 	{ XFUNC_mv_end,			0,  CTRL_E	},
   1060 	{ XFUNC_mv_beg,			0,  CTRL_A	},
   1061 	{ XFUNC_draw_line,		0,  CTRL_L	},
   1062 	{ XFUNC_cls,			1,  CTRL_L	},
   1063 	{ XFUNC_meta1,			0,  CTRL_BO	},
   1064 	{ XFUNC_meta2,			0,  CTRL_X	},
   1065 	{ XFUNC_kill,			0,  CTRL_K	},
   1066 	{ XFUNC_yank,			0,  CTRL_Y	},
   1067 	{ XFUNC_meta_yank,		1,	'y'	},
   1068 	{ XFUNC_literal,		0,  CTRL_CA	},
   1069 	{ XFUNC_comment,		1,	'#'	},
   1070 	{ XFUNC_transpose,		0,  CTRL_T	},
   1071 	{ XFUNC_complete,		1,  CTRL_BO	},
   1072 	{ XFUNC_comp_list,		0,  CTRL_I	},
   1073 	{ XFUNC_comp_list,		1,	'='	},
   1074 	{ XFUNC_enumerate,		1,	'?'	},
   1075 	{ XFUNC_expand,			1,	'*'	},
   1076 	{ XFUNC_comp_file,		1,  CTRL_X	},
   1077 	{ XFUNC_comp_comm,		2,  CTRL_BO	},
   1078 	{ XFUNC_list_comm,		2,	'?'	},
   1079 	{ XFUNC_list_file,		2,  CTRL_Y	},
   1080 	{ XFUNC_set_mark,		1,	' '	},
   1081 	{ XFUNC_kill_region,		0,  CTRL_W	},
   1082 	{ XFUNC_xchg_point_mark,	2,  CTRL_X	},
   1083 	{ XFUNC_literal,		0,  CTRL_V	},
   1084 	{ XFUNC_version,		1,  CTRL_V	},
   1085 	{ XFUNC_prev_histword,		1,	'.'	},
   1086 	{ XFUNC_prev_histword,		1,	'_'	},
   1087 	{ XFUNC_set_arg,		1,	'0'	},
   1088 	{ XFUNC_set_arg,		1,	'1'	},
   1089 	{ XFUNC_set_arg,		1,	'2'	},
   1090 	{ XFUNC_set_arg,		1,	'3'	},
   1091 	{ XFUNC_set_arg,		1,	'4'	},
   1092 	{ XFUNC_set_arg,		1,	'5'	},
   1093 	{ XFUNC_set_arg,		1,	'6'	},
   1094 	{ XFUNC_set_arg,		1,	'7'	},
   1095 	{ XFUNC_set_arg,		1,	'8'	},
   1096 	{ XFUNC_set_arg,		1,	'9'	},
   1097 #ifndef MKSH_SMALL
   1098 	{ XFUNC_fold_upper,		1,	'U'	},
   1099 	{ XFUNC_fold_upper,		1,	'u'	},
   1100 	{ XFUNC_fold_lower,		1,	'L'	},
   1101 	{ XFUNC_fold_lower,		1,	'l'	},
   1102 	{ XFUNC_fold_capitalise,	1,	'C'	},
   1103 	{ XFUNC_fold_capitalise,	1,	'c'	},
   1104 #endif
   1105 	/*
   1106 	 * These for ANSI arrow keys: arguablely shouldn't be here by
   1107 	 * default, but its simpler/faster/smaller than using termcap
   1108 	 * entries.
   1109 	 */
   1110 	{ XFUNC_meta2,			1,	'['	},
   1111 	{ XFUNC_meta2,			1,	'O'	},
   1112 	{ XFUNC_prev_com,		2,	'A'	},
   1113 	{ XFUNC_next_com,		2,	'B'	},
   1114 	{ XFUNC_mv_forw,		2,	'C'	},
   1115 	{ XFUNC_mv_back,		2,	'D'	},
   1116 #ifndef MKSH_SMALL
   1117 	{ XFUNC_vt_hack,		2,	'1'	},
   1118 	{ XFUNC_mv_beg | 0x80,		2,	'7'	},
   1119 	{ XFUNC_mv_beg,			2,	'H'	},
   1120 	{ XFUNC_mv_end | 0x80,		2,	'4'	},
   1121 	{ XFUNC_mv_end | 0x80,		2,	'8'	},
   1122 	{ XFUNC_mv_end,			2,	'F'	},
   1123 	{ XFUNC_del_char | 0x80,	2,	'3'	},
   1124 	{ XFUNC_del_char,		2,	'P'	},
   1125 	{ XFUNC_search_hist_up | 0x80,	2,	'5'	},
   1126 	{ XFUNC_search_hist_dn | 0x80,	2,	'6'	},
   1127 #endif
   1128 	/* PC scancodes */
   1129 #if !defined(MKSH_SMALL) || defined(__OS2__)
   1130 	{ XFUNC_meta3,			0,	0	},
   1131 	{ XFUNC_mv_beg,			3,	71	},
   1132 	{ XFUNC_prev_com,		3,	72	},
   1133 #ifndef MKSH_SMALL
   1134 	{ XFUNC_search_hist_up,		3,	73	},
   1135 #endif
   1136 	{ XFUNC_mv_back,		3,	75	},
   1137 	{ XFUNC_mv_forw,		3,	77	},
   1138 	{ XFUNC_mv_end,			3,	79	},
   1139 	{ XFUNC_next_com,		3,	80	},
   1140 #ifndef MKSH_SMALL
   1141 	{ XFUNC_search_hist_dn,		3,	81	},
   1142 #endif
   1143 	{ XFUNC_del_char,		3,	83	},
   1144 #endif
   1145 #ifndef MKSH_SMALL
   1146 	/* more non-standard ones */
   1147 	{ XFUNC_eval_region,		1,  CTRL_E	},
   1148 	{ XFUNC_edit_line,		2,	'e'	}
   1149 #endif
   1150 };
   1151 
   1152 static size_t
   1153 x_nb2nc(size_t nb)
   1154 {
   1155 	char *cp;
   1156 	size_t nc = 0;
   1157 
   1158 	for (cp = xcp; cp < (xcp + nb); ++nc)
   1159 		cp += utf_ptradj(cp);
   1160 	return (nc);
   1161 }
   1162 
   1163 static void
   1164 x_modified(void)
   1165 {
   1166 	if (!modified) {
   1167 		x_histmcp = x_histp;
   1168 		x_histp = histptr + 1;
   1169 		modified = 1;
   1170 	}
   1171 }
   1172 
   1173 #ifdef MKSH_SMALL
   1174 #define XFUNC_VALUE(f) (f)
   1175 #else
   1176 #define XFUNC_VALUE(f) (f & 0x7F)
   1177 #endif
   1178 
   1179 static int
   1180 x_e_getmbc(char *sbuf)
   1181 {
   1182 	int c, pos = 0;
   1183 	unsigned char *buf = (unsigned char *)sbuf;
   1184 
   1185 	memset(buf, 0, 4);
   1186 	buf[pos++] = c = x_e_getc();
   1187 	if (c == -1)
   1188 		return (-1);
   1189 	if (UTFMODE) {
   1190 		if ((rtt2asc(buf[0]) >= (unsigned char)0xC2) &&
   1191 		    (rtt2asc(buf[0]) < (unsigned char)0xF0)) {
   1192 			c = x_e_getc();
   1193 			if (c == -1)
   1194 				return (-1);
   1195 			if ((rtt2asc(c) & 0xC0) != 0x80) {
   1196 				x_e_ungetc(c);
   1197 				return (1);
   1198 			}
   1199 			buf[pos++] = c;
   1200 		}
   1201 		if ((rtt2asc(buf[0]) >= (unsigned char)0xE0) &&
   1202 		    (rtt2asc(buf[0]) < (unsigned char)0xF0)) {
   1203 			/* XXX x_e_ungetc is one-octet only */
   1204 			buf[pos++] = c = x_e_getc();
   1205 			if (c == -1)
   1206 				return (-1);
   1207 		}
   1208 	}
   1209 	return (pos);
   1210 }
   1211 
   1212 /*
   1213  * minimum required space to work with on a line - if the prompt
   1214  * leaves less space than this on a line, the prompt is truncated
   1215  */
   1216 #define MIN_EDIT_SPACE	7
   1217 
   1218 static void
   1219 x_init_prompt(bool doprint)
   1220 {
   1221 	prompt_trunc = pprompt(prompt, doprint ? 0 : -1);
   1222 	pwidth = prompt_trunc % x_cols;
   1223 	prompt_trunc -= pwidth;
   1224 	if ((mksh_uari_t)pwidth > ((mksh_uari_t)x_cols - 3 - MIN_EDIT_SPACE)) {
   1225 		/* force newline after prompt */
   1226 		prompt_trunc = -1;
   1227 		pwidth = 0;
   1228 		if (doprint)
   1229 			x_e_putc2('\n');
   1230 	}
   1231 }
   1232 
   1233 static int
   1234 x_emacs(char *buf)
   1235 {
   1236 	int c, i;
   1237 	unsigned char f;
   1238 
   1239 	xbp = xbuf = buf;
   1240 	xend = buf + LINE;
   1241 	xlp = xcp = xep = buf;
   1242 	*xcp = 0;
   1243 	xlp_valid = true;
   1244 	xmp = NULL;
   1245 	x_curprefix = 0;
   1246 	x_histmcp = x_histp = histptr + 1;
   1247 	x_last_command = XFUNC_error;
   1248 
   1249 	x_init_prompt(true);
   1250 	x_displen = (xx_cols = x_cols) - 2 - (x_col = pwidth);
   1251 	x_adj_done = 0;
   1252 	x_adj_ok = true;
   1253 
   1254 	x_histncp = NULL;
   1255 	if (x_nextcmd >= 0) {
   1256 		int off = source->line - x_nextcmd;
   1257 		if (histptr - history >= off) {
   1258 			x_load_hist(histptr - off);
   1259 			x_histncp = x_histp;
   1260 		}
   1261 		x_nextcmd = -1;
   1262 	}
   1263 	editmode = 1;
   1264 	while (/* CONSTCOND */ 1) {
   1265 		x_flush();
   1266 		if ((c = x_e_getc()) < 0)
   1267 			return (0);
   1268 
   1269 		f = x_curprefix == -1 ? XFUNC_insert :
   1270 		    x_tab[x_curprefix][c];
   1271 #ifndef MKSH_SMALL
   1272 		if (f & 0x80) {
   1273 			f &= 0x7F;
   1274 			if ((i = x_e_getc()) != '~')
   1275 				x_e_ungetc(i);
   1276 		}
   1277 
   1278 		/* avoid bind key macro recursion */
   1279 		if (macroptr && f == XFUNC_ins_string)
   1280 			f = XFUNC_insert;
   1281 #endif
   1282 
   1283 		if (!(x_ftab[f].xf_flags & XF_PREFIX) &&
   1284 		    x_last_command != XFUNC_set_arg) {
   1285 			x_arg = 1;
   1286 			x_arg_defaulted = true;
   1287 		}
   1288 		i = c | (x_curprefix << 8);
   1289 		x_curprefix = 0;
   1290 		switch ((*x_ftab[f].xf_func)(i)) {
   1291 		case KSTD:
   1292 			if (!(x_ftab[f].xf_flags & XF_PREFIX))
   1293 				x_last_command = f;
   1294 			break;
   1295 		case KEOL:
   1296 			i = xep - xbuf;
   1297 			return (i);
   1298 		case KINTR:
   1299 			/* special case for interrupt */
   1300 			x_intr(SIGINT, c);
   1301 		}
   1302 		/* ad-hoc hack for fixing the cursor position */
   1303 		x_goto(xcp);
   1304 	}
   1305 }
   1306 
   1307 static int
   1308 x_insert(int c)
   1309 {
   1310 	static int left, pos, save_arg;
   1311 	static char str[4];
   1312 
   1313 	/*
   1314 	 * Should allow tab and control chars.
   1315 	 */
   1316 	if (c == 0) {
   1317  invmbs:
   1318 		left = 0;
   1319 		x_e_putc2(KSH_BEL);
   1320 		return (KSTD);
   1321 	}
   1322 	if (UTFMODE) {
   1323 		if (((rtt2asc(c) & 0xC0) == 0x80) && left) {
   1324 			str[pos++] = c;
   1325 			if (!--left) {
   1326 				str[pos] = '\0';
   1327 				x_arg = save_arg;
   1328 				while (x_arg--)
   1329 					x_ins(str);
   1330 			}
   1331 			return (KSTD);
   1332 		}
   1333 		if (left) {
   1334 			if (x_curprefix == -1) {
   1335 				/* flush invalid multibyte */
   1336 				str[pos] = '\0';
   1337 				while (save_arg--)
   1338 					x_ins(str);
   1339 			}
   1340 		}
   1341 		if ((c >= 0xC2) && (c < 0xE0))
   1342 			left = 1;
   1343 		else if ((c >= 0xE0) && (c < 0xF0))
   1344 			left = 2;
   1345 		else if (c > 0x7F)
   1346 			goto invmbs;
   1347 		else
   1348 			left = 0;
   1349 		if (left) {
   1350 			save_arg = x_arg;
   1351 			pos = 1;
   1352 			str[0] = c;
   1353 			return (KSTD);
   1354 		}
   1355 	}
   1356 	left = 0;
   1357 	str[0] = c;
   1358 	str[1] = '\0';
   1359 	while (x_arg--)
   1360 		x_ins(str);
   1361 	return (KSTD);
   1362 }
   1363 
   1364 #ifndef MKSH_SMALL
   1365 static int
   1366 x_ins_string(int c)
   1367 {
   1368 	macroptr = x_atab[c >> 8][c & 255];
   1369 	/*
   1370 	 * we no longer need to bother checking if macroptr is
   1371 	 * not NULL but first char is NUL; x_e_getc() does it
   1372 	 */
   1373 	return (KSTD);
   1374 }
   1375 #endif
   1376 
   1377 static int
   1378 x_do_ins(const char *cp, size_t len)
   1379 {
   1380 	if (xep + len >= xend) {
   1381 		x_e_putc2(KSH_BEL);
   1382 		return (-1);
   1383 	}
   1384 	memmove(xcp + len, xcp, xep - xcp + 1);
   1385 	memmove(xcp, cp, len);
   1386 	xcp += len;
   1387 	xep += len;
   1388 	x_modified();
   1389 	return (0);
   1390 }
   1391 
   1392 static int
   1393 x_ins(const char *s)
   1394 {
   1395 	char *cp = xcp;
   1396 	int adj = x_adj_done;
   1397 
   1398 	if (x_do_ins(s, strlen(s)) < 0)
   1399 		return (-1);
   1400 	/*
   1401 	 * x_zots() may result in a call to x_adjust()
   1402 	 * we want xcp to reflect the new position.
   1403 	 */
   1404 	xlp_valid = false;
   1405 	x_lastcp();
   1406 	x_adj_ok = tobool(xcp >= xlp);
   1407 	x_zots(cp);
   1408 	if (adj == x_adj_done)
   1409 		/* x_adjust() has not been called */
   1410 		x_lastpos();
   1411 	x_adj_ok = true;
   1412 	return (0);
   1413 }
   1414 
   1415 static int
   1416 x_del_back(int c MKSH_A_UNUSED)
   1417 {
   1418 	ssize_t i = 0;
   1419 
   1420 	if (xcp == xbuf) {
   1421 		x_e_putc2(KSH_BEL);
   1422 		return (KSTD);
   1423 	}
   1424 	do {
   1425 		x_goto(xcp - 1);
   1426 	} while ((++i < x_arg) && (xcp != xbuf));
   1427 	x_delete(i, false);
   1428 	return (KSTD);
   1429 }
   1430 
   1431 static int
   1432 x_del_char(int c MKSH_A_UNUSED)
   1433 {
   1434 	char *cp, *cp2;
   1435 	size_t i = 0;
   1436 
   1437 	cp = xcp;
   1438 	while (i < (size_t)x_arg) {
   1439 		utf_ptradjx(cp, cp2);
   1440 		if (cp2 > xep)
   1441 			break;
   1442 		cp = cp2;
   1443 		i++;
   1444 	}
   1445 
   1446 	if (!i) {
   1447 		x_e_putc2(KSH_BEL);
   1448 		return (KSTD);
   1449 	}
   1450 	x_delete(i, false);
   1451 	return (KSTD);
   1452 }
   1453 
   1454 /* Delete nc chars to the right of the cursor (including cursor position) */
   1455 static void
   1456 x_delete(size_t nc, bool push)
   1457 {
   1458 	size_t i, nb, nw;
   1459 	char *cp;
   1460 
   1461 	if (nc == 0)
   1462 		return;
   1463 
   1464 	nw = 0;
   1465 	cp = xcp;
   1466 	for (i = 0; i < nc; ++i) {
   1467 		char *cp2;
   1468 		int j;
   1469 
   1470 		j = x_size2(cp, &cp2);
   1471 		if (cp2 > xep)
   1472 			break;
   1473 		cp = cp2;
   1474 		nw += j;
   1475 	}
   1476 	nb = cp - xcp;
   1477 	/* nc = i; */
   1478 
   1479 	if (xmp != NULL && xmp > xcp) {
   1480 		if (xcp + nb > xmp)
   1481 			xmp = xcp;
   1482 		else
   1483 			xmp -= nb;
   1484 	}
   1485 	/*
   1486 	 * This lets us yank a word we have deleted.
   1487 	 */
   1488 	if (push)
   1489 		x_push(nb);
   1490 
   1491 	xep -= nb;
   1492 	/* Copies the NUL */
   1493 	memmove(xcp, xcp + nb, xep - xcp + 1);
   1494 	/* don't redraw */
   1495 	x_adj_ok = false;
   1496 	xlp_valid = false;
   1497 	x_zots(xcp);
   1498 	/*
   1499 	 * if we are already filling the line,
   1500 	 * there is no need to ' ', '\b'.
   1501 	 * But if we must, make sure we do the minimum.
   1502 	 */
   1503 	if ((i = xx_cols - 2 - x_col) > 0 || xep - xlp == 0) {
   1504 		nw = i = (nw < i) ? nw : i;
   1505 		while (i--)
   1506 			x_e_putc2(' ');
   1507 		if (x_col == xx_cols - 2) {
   1508 			x_e_putc2((xep > xlp) ? '>' : (xbp > xbuf) ? '<' : ' ');
   1509 			++nw;
   1510 		}
   1511 		while (nw--)
   1512 			x_e_putc2('\b');
   1513 	}
   1514 	/*x_goto(xcp);*/
   1515 	x_adj_ok = true;
   1516 	xlp_valid = false;
   1517 	x_lastpos();
   1518 	x_modified();
   1519 	return;
   1520 }
   1521 
   1522 static int
   1523 x_del_bword(int c MKSH_A_UNUSED)
   1524 {
   1525 	x_delete(x_bword(), true);
   1526 	return (KSTD);
   1527 }
   1528 
   1529 static int
   1530 x_mv_bword(int c MKSH_A_UNUSED)
   1531 {
   1532 	x_bword();
   1533 	return (KSTD);
   1534 }
   1535 
   1536 static int
   1537 x_mv_fword(int c MKSH_A_UNUSED)
   1538 {
   1539 	x_fword(true);
   1540 	return (KSTD);
   1541 }
   1542 
   1543 static int
   1544 x_del_fword(int c MKSH_A_UNUSED)
   1545 {
   1546 	x_delete(x_fword(false), true);
   1547 	return (KSTD);
   1548 }
   1549 
   1550 static size_t
   1551 x_bword(void)
   1552 {
   1553 	size_t nb = 0;
   1554 	char *cp = xcp;
   1555 
   1556 	if (cp == xbuf) {
   1557 		x_e_putc2(KSH_BEL);
   1558 		return (0);
   1559 	}
   1560 	while (x_arg--) {
   1561 		while (cp != xbuf && ctype(cp[-1], C_MFS)) {
   1562 			cp--;
   1563 			nb++;
   1564 		}
   1565 		while (cp != xbuf && !ctype(cp[-1], C_MFS)) {
   1566 			cp--;
   1567 			nb++;
   1568 		}
   1569 	}
   1570 	x_goto(cp);
   1571 	return (x_nb2nc(nb));
   1572 }
   1573 
   1574 static size_t
   1575 x_fword(bool move)
   1576 {
   1577 	size_t nc;
   1578 	char *cp = xcp;
   1579 
   1580 	if (cp == xep) {
   1581 		x_e_putc2(KSH_BEL);
   1582 		return (0);
   1583 	}
   1584 	while (x_arg--) {
   1585 		while (cp != xep && ctype(*cp, C_MFS))
   1586 			cp++;
   1587 		while (cp != xep && !ctype(*cp, C_MFS))
   1588 			cp++;
   1589 	}
   1590 	nc = x_nb2nc(cp - xcp);
   1591 	if (move)
   1592 		x_goto(cp);
   1593 	return (nc);
   1594 }
   1595 
   1596 static void
   1597 x_goto(char *cp)
   1598 {
   1599 	cp = cp >= xep ? xep : x_bs0(cp, xbuf);
   1600 	if (cp < xbp || cp >= utf_skipcols(xbp, x_displen, NULL)) {
   1601 		/* we are heading off screen */
   1602 		xcp = cp;
   1603 		x_adjust();
   1604 	} else if (cp < xcp) {
   1605 		/* move back */
   1606 		while (cp < xcp)
   1607 			x_bs3(&xcp);
   1608 	} else if (cp > xcp) {
   1609 		/* move forward */
   1610 		while (cp > xcp)
   1611 			x_zotc3(&xcp);
   1612 	}
   1613 }
   1614 
   1615 static char *
   1616 x_bs0(char *cp, char *lower_bound)
   1617 {
   1618 	if (UTFMODE)
   1619 		while ((!lower_bound || (cp > lower_bound)) &&
   1620 		    ((rtt2asc(*cp) & 0xC0) == 0x80))
   1621 			--cp;
   1622 	return (cp);
   1623 }
   1624 
   1625 static void
   1626 x_bs3(char **p)
   1627 {
   1628 	int i;
   1629 
   1630 	*p = x_bs0((*p) - 1, NULL);
   1631 	i = x_size2(*p, NULL);
   1632 	while (i--)
   1633 		x_e_putc2('\b');
   1634 }
   1635 
   1636 static int
   1637 x_size2(char *cp, char **dcp)
   1638 {
   1639 	uint8_t c = *(unsigned char *)cp;
   1640 
   1641 	if (UTFMODE && (rtt2asc(c) > 0x7F))
   1642 		return (utf_widthadj(cp, (const char **)dcp));
   1643 	if (dcp)
   1644 		*dcp = cp + 1;
   1645 	if (c == '\t')
   1646 		/* Kludge, tabs are always four spaces. */
   1647 		return (4);
   1648 	if (ksh_isctrl(c))
   1649 		/* control unsigned char */
   1650 		return (2);
   1651 	return (1);
   1652 }
   1653 
   1654 static void
   1655 x_zots(char *str)
   1656 {
   1657 	int adj = x_adj_done;
   1658 
   1659 	x_lastcp();
   1660 	while (*str && str < xlp && x_col < xx_cols && adj == x_adj_done)
   1661 		x_zotc3(&str);
   1662 }
   1663 
   1664 static void
   1665 x_zotc3(char **cp)
   1666 {
   1667 	unsigned char c = **(unsigned char **)cp;
   1668 
   1669 	if (c == '\t') {
   1670 		/* Kludge, tabs are always four spaces. */
   1671 		x_e_puts(T4spaces);
   1672 		(*cp)++;
   1673 	} else if (ksh_isctrl(c)) {
   1674 		x_e_putc2('^');
   1675 		x_e_putc2(ksh_unctrl(c));
   1676 		(*cp)++;
   1677 	} else
   1678 		x_e_putc3((const char **)cp);
   1679 }
   1680 
   1681 static int
   1682 x_mv_back(int c MKSH_A_UNUSED)
   1683 {
   1684 	if (xcp == xbuf) {
   1685 		x_e_putc2(KSH_BEL);
   1686 		return (KSTD);
   1687 	}
   1688 	while (x_arg--) {
   1689 		x_goto(xcp - 1);
   1690 		if (xcp == xbuf)
   1691 			break;
   1692 	}
   1693 	return (KSTD);
   1694 }
   1695 
   1696 static int
   1697 x_mv_forw(int c MKSH_A_UNUSED)
   1698 {
   1699 	char *cp = xcp, *cp2;
   1700 
   1701 	if (xcp == xep) {
   1702 		x_e_putc2(KSH_BEL);
   1703 		return (KSTD);
   1704 	}
   1705 	while (x_arg--) {
   1706 		utf_ptradjx(cp, cp2);
   1707 		if (cp2 > xep)
   1708 			break;
   1709 		cp = cp2;
   1710 	}
   1711 	x_goto(cp);
   1712 	return (KSTD);
   1713 }
   1714 
   1715 static int
   1716 x_search_char_forw(int c MKSH_A_UNUSED)
   1717 {
   1718 	char *cp = xcp;
   1719 	char tmp[4];
   1720 
   1721 	*xep = '\0';
   1722 	if (x_e_getmbc(tmp) < 0) {
   1723 		x_e_putc2(KSH_BEL);
   1724 		return (KSTD);
   1725 	}
   1726 	while (x_arg--) {
   1727 		if ((cp = (cp == xep) ? NULL : strstr(cp + 1, tmp)) == NULL &&
   1728 		    (cp = strstr(xbuf, tmp)) == NULL) {
   1729 			x_e_putc2(KSH_BEL);
   1730 			return (KSTD);
   1731 		}
   1732 	}
   1733 	x_goto(cp);
   1734 	return (KSTD);
   1735 }
   1736 
   1737 static int
   1738 x_search_char_back(int c MKSH_A_UNUSED)
   1739 {
   1740 	char *cp = xcp, *p, tmp[4];
   1741 	bool b;
   1742 
   1743 	if (x_e_getmbc(tmp) < 0) {
   1744 		x_e_putc2(KSH_BEL);
   1745 		return (KSTD);
   1746 	}
   1747 	for (; x_arg--; cp = p)
   1748 		for (p = cp; ; ) {
   1749 			if (p-- == xbuf)
   1750 				p = xep;
   1751 			if (p == cp) {
   1752 				x_e_putc2(KSH_BEL);
   1753 				return (KSTD);
   1754 			}
   1755 			if ((tmp[1] && ((p+1) > xep)) ||
   1756 			    (tmp[2] && ((p+2) > xep)))
   1757 				continue;
   1758 			b = true;
   1759 			if (*p != tmp[0])
   1760 				b = false;
   1761 			if (b && tmp[1] && p[1] != tmp[1])
   1762 				b = false;
   1763 			if (b && tmp[2] && p[2] != tmp[2])
   1764 				b = false;
   1765 			if (b)
   1766 				break;
   1767 		}
   1768 	x_goto(cp);
   1769 	return (KSTD);
   1770 }
   1771 
   1772 static int
   1773 x_newline(int c MKSH_A_UNUSED)
   1774 {
   1775 	x_e_putc2('\r');
   1776 	x_e_putc2('\n');
   1777 	x_flush();
   1778 	*xep++ = '\n';
   1779 	return (KEOL);
   1780 }
   1781 
   1782 static int
   1783 x_end_of_text(int c MKSH_A_UNUSED)
   1784 {
   1785 	unsigned char tmp[1], *cp = tmp;
   1786 
   1787 	*tmp = isedchar(edchars.eof) ? (unsigned char)edchars.eof :
   1788 	    (unsigned char)CTRL_D;
   1789 	x_zotc3((char **)&cp);
   1790 	x_putc('\r');
   1791 	x_putc('\n');
   1792 	x_flush();
   1793 	return (KEOL);
   1794 }
   1795 
   1796 static int
   1797 x_beg_hist(int c MKSH_A_UNUSED)
   1798 {
   1799 	x_load_hist(history);
   1800 	return (KSTD);
   1801 }
   1802 
   1803 static int
   1804 x_end_hist(int c MKSH_A_UNUSED)
   1805 {
   1806 	x_load_hist(histptr);
   1807 	return (KSTD);
   1808 }
   1809 
   1810 static int
   1811 x_prev_com(int c MKSH_A_UNUSED)
   1812 {
   1813 	x_load_hist(x_histp - x_arg);
   1814 	return (KSTD);
   1815 }
   1816 
   1817 static int
   1818 x_next_com(int c MKSH_A_UNUSED)
   1819 {
   1820 	x_load_hist(x_histp + x_arg);
   1821 	return (KSTD);
   1822 }
   1823 
   1824 /*
   1825  * Goto a particular history number obtained from argument.
   1826  * If no argument is given history 1 is probably not what you
   1827  * want so we'll simply go to the oldest one.
   1828  */
   1829 static int
   1830 x_goto_hist(int c MKSH_A_UNUSED)
   1831 {
   1832 	if (x_arg_defaulted)
   1833 		x_load_hist(history);
   1834 	else
   1835 		x_load_hist(histptr + x_arg - source->line);
   1836 	return (KSTD);
   1837 }
   1838 
   1839 static void
   1840 x_load_hist(char **hp)
   1841 {
   1842 	char *sp = NULL;
   1843 
   1844 	if (hp == histptr + 1) {
   1845 		sp = holdbufp;
   1846 		modified = 0;
   1847 	} else if (hp < history || hp > histptr) {
   1848 		x_e_putc2(KSH_BEL);
   1849 		return;
   1850 	}
   1851 	if (sp == NULL)
   1852 		sp = *hp;
   1853 	x_histp = hp;
   1854 	if (modified)
   1855 		strlcpy(holdbufp, xbuf, LINE);
   1856 	strlcpy(xbuf, sp, xend - xbuf);
   1857 	xbp = xbuf;
   1858 	xep = xcp = strnul(xbuf);
   1859 	x_adjust();
   1860 	modified = 0;
   1861 }
   1862 
   1863 static int
   1864 x_nl_next_com(int c MKSH_A_UNUSED)
   1865 {
   1866 	if (!modified)
   1867 		x_histmcp = x_histp;
   1868 	if (!x_histncp || (x_histmcp != x_histncp && x_histmcp != histptr + 1))
   1869 		/* fresh start of ^O */
   1870 		x_histncp = x_histmcp;
   1871 	x_nextcmd = source->line - (histptr - x_histncp) + 1;
   1872 	return (x_newline('\n'));
   1873 }
   1874 
   1875 static int
   1876 x_eot_del(int c)
   1877 {
   1878 	if (xep == xbuf && x_arg_defaulted)
   1879 		return (x_end_of_text(c));
   1880 	else
   1881 		return (x_del_char(c));
   1882 }
   1883 
   1884 /* reverse incremental history search */
   1885 static int
   1886 x_search_hist(int c)
   1887 {
   1888 	int offset = -1;	/* offset of match in xbuf, else -1 */
   1889 	char pat[80 + 1];	/* pattern buffer */
   1890 	char *p = pat;
   1891 	unsigned char f;
   1892 
   1893 	*p = '\0';
   1894 	while (/* CONSTCOND */ 1) {
   1895 		if (offset < 0) {
   1896 			x_e_puts("\nI-search: ");
   1897 			x_e_puts(pat);
   1898 		}
   1899 		x_flush();
   1900 		if ((c = x_e_getc()) < 0)
   1901 			return (KSTD);
   1902 		f = x_tab[0][c];
   1903 		if (c == CTRL_BO) {
   1904 			if ((f & 0x7F) == XFUNC_meta1) {
   1905 				if ((c = x_e_getc()) < 0)
   1906 					return (KSTD);
   1907 				f = x_tab[1][c] & 0x7F;
   1908 				if (f == XFUNC_meta1 || f == XFUNC_meta2)
   1909 					x_meta1(CTRL_BO);
   1910 				x_e_ungetc(c);
   1911 			}
   1912 			break;
   1913 		}
   1914 #ifndef MKSH_SMALL
   1915 		if (f & 0x80) {
   1916 			f &= 0x7F;
   1917 			if ((c = x_e_getc()) != '~')
   1918 				x_e_ungetc(c);
   1919 		}
   1920 #endif
   1921 		if (f == XFUNC_search_hist)
   1922 			offset = x_search(pat, 0, offset);
   1923 		else if (f == XFUNC_del_back) {
   1924 			if (p == pat) {
   1925 				offset = -1;
   1926 				break;
   1927 			}
   1928 			if (p > pat) {
   1929 				p = x_bs0(p - 1, pat);
   1930 				*p = '\0';
   1931 			}
   1932 			if (p == pat)
   1933 				offset = -1;
   1934 			else
   1935 				offset = x_search(pat, 1, offset);
   1936 			continue;
   1937 		} else if (f == XFUNC_insert) {
   1938 			/* add char to pattern */
   1939 			/* overflow check... */
   1940 			if ((size_t)(p - pat) >= sizeof(pat) - 1) {
   1941 				x_e_putc2(KSH_BEL);
   1942 				continue;
   1943 			}
   1944 			*p++ = c, *p = '\0';
   1945 			if (offset >= 0) {
   1946 				/* already have partial match */
   1947 				offset = x_match(xbuf, pat);
   1948 				if (offset >= 0) {
   1949 					x_goto(xbuf + offset + (p - pat) -
   1950 					    (*pat == '^'));
   1951 					continue;
   1952 				}
   1953 			}
   1954 			offset = x_search(pat, 0, offset);
   1955 		} else if (f == XFUNC_abort) {
   1956 			if (offset >= 0)
   1957 				x_load_hist(histptr + 1);
   1958 			break;
   1959 		} else {
   1960 			/* other command */
   1961 			x_e_ungetc(c);
   1962 			break;
   1963 		}
   1964 	}
   1965 	if (offset < 0)
   1966 		x_redraw('\n');
   1967 	return (KSTD);
   1968 }
   1969 
   1970 /* search backward from current line */
   1971 static int
   1972 x_search(char *pat, int sameline, int offset)
   1973 {
   1974 	char **hp;
   1975 	int i;
   1976 
   1977 	for (hp = x_histp - (sameline ? 0 : 1); hp >= history; --hp) {
   1978 		i = x_match(*hp, pat);
   1979 		if (i >= 0) {
   1980 			if (offset < 0)
   1981 				x_e_putc2('\n');
   1982 			x_load_hist(hp);
   1983 			x_goto(xbuf + i + strlen(pat) - (*pat == '^'));
   1984 			return (i);
   1985 		}
   1986 	}
   1987 	x_e_putc2(KSH_BEL);
   1988 	x_histp = histptr;
   1989 	return (-1);
   1990 }
   1991 
   1992 #ifndef MKSH_SMALL
   1993 /* anchored search up from current line */
   1994 static int
   1995 x_search_hist_up(int c MKSH_A_UNUSED)
   1996 {
   1997 	return (x_search_dir(-1));
   1998 }
   1999 
   2000 /* anchored search down from current line */
   2001 static int
   2002 x_search_hist_dn(int c MKSH_A_UNUSED)
   2003 {
   2004 	return (x_search_dir(1));
   2005 }
   2006 
   2007 /* anchored search in the indicated direction */
   2008 static int
   2009 x_search_dir(int search_dir /* should've been bool */)
   2010 {
   2011 	char **hp = x_histp + search_dir;
   2012 	size_t curs = xcp - xbuf;
   2013 
   2014 	while (histptr >= hp && hp >= history) {
   2015 		if (strncmp(xbuf, *hp, curs) == 0) {
   2016 			x_load_hist(hp);
   2017 			x_goto(xbuf + curs);
   2018 			break;
   2019 		}
   2020 		hp += search_dir;
   2021 	}
   2022 	return (KSTD);
   2023 }
   2024 #endif
   2025 
   2026 /* return position of first match of pattern in string, else -1 */
   2027 static int
   2028 x_match(char *str, char *pat)
   2029 {
   2030 	if (*pat == '^') {
   2031 		return ((strncmp(str, pat + 1, strlen(pat + 1)) == 0) ? 0 : -1);
   2032 	} else {
   2033 		char *q = strstr(str, pat);
   2034 		return ((q == NULL) ? -1 : q - str);
   2035 	}
   2036 }
   2037 
   2038 static int
   2039 x_del_line(int c MKSH_A_UNUSED)
   2040 {
   2041 	*xep = 0;
   2042 	x_push(xep - (xcp = xbuf));
   2043 	xlp = xbp = xep = xbuf;
   2044 	xlp_valid = true;
   2045 	*xcp = 0;
   2046 	xmp = NULL;
   2047 	x_redraw('\r');
   2048 	x_modified();
   2049 	return (KSTD);
   2050 }
   2051 
   2052 static int
   2053 x_mv_end(int c MKSH_A_UNUSED)
   2054 {
   2055 	x_goto(xep);
   2056 	return (KSTD);
   2057 }
   2058 
   2059 static int
   2060 x_mv_beg(int c MKSH_A_UNUSED)
   2061 {
   2062 	x_goto(xbuf);
   2063 	return (KSTD);
   2064 }
   2065 
   2066 static int
   2067 x_draw_line(int c MKSH_A_UNUSED)
   2068 {
   2069 	x_redraw('\n');
   2070 	return (KSTD);
   2071 }
   2072 
   2073 static int
   2074 x_cls(int c MKSH_A_UNUSED)
   2075 {
   2076 	shf_puts(MKSH_CLS_STRING, shl_out);
   2077 	x_redraw(0);
   2078 	return (KSTD);
   2079 }
   2080 
   2081 /*
   2082  * clear line from x_col (current cursor position) to xx_cols - 2,
   2083  * then output lastch, then go back to x_col; if lastch is space,
   2084  * clear with termcap instead of spaces, or not if line_was_cleared;
   2085  * lastch MUST be an ASCII character with wcwidth(lastch) == 1
   2086  */
   2087 static void
   2088 x_clrtoeol(int lastch, bool line_was_cleared)
   2089 {
   2090 	int col;
   2091 
   2092 	if (lastch == ' ' && !line_was_cleared && x_term_mode == 1) {
   2093 		shf_puts(KSH_ESC_STRING "[K", shl_out);
   2094 		line_was_cleared = true;
   2095 	}
   2096 	if (lastch == ' ' && line_was_cleared)
   2097 		return;
   2098 
   2099 	col = x_col;
   2100 	while (col < (xx_cols - 2)) {
   2101 		x_putc(' ');
   2102 		++col;
   2103 	}
   2104 	x_putc(lastch);
   2105 	++col;
   2106 	while (col > x_col) {
   2107 		x_putc('\b');
   2108 		--col;
   2109 	}
   2110 }
   2111 
   2112 /* output the prompt, assuming a line has just been started */
   2113 static void
   2114 x_pprompt(void)
   2115 {
   2116 	if (prompt_trunc != -1)
   2117 		pprompt(prompt, prompt_trunc);
   2118 	x_col = pwidth;
   2119 }
   2120 
   2121 /* output CR, then redraw the line, clearing to EOL if needed (cr  0, LF) */
   2122 static void
   2123 x_redraw(int cr)
   2124 {
   2125 	int lch;
   2126 
   2127 	x_adj_ok = false;
   2128 	/* clear the line */
   2129 	x_e_putc2(cr ? cr : '\r');
   2130 	x_flush();
   2131 	/* display the prompt */
   2132 	if (xbp == xbuf)
   2133 		x_pprompt();
   2134 	x_displen = xx_cols - 2 - x_col;
   2135 	/* display the line content */
   2136 	xlp_valid = false;
   2137 	x_zots(xbp);
   2138 	/* check whether there is more off-screen */
   2139 	lch = xep > xlp ? (xbp > xbuf ? '*' : '>') : (xbp > xbuf) ? '<' : ' ';
   2140 	/* clear the rest of the line */
   2141 	x_clrtoeol(lch, !cr || cr == '\n');
   2142 	/* go back to actual cursor position */
   2143 	x_lastpos();
   2144 	x_adj_ok = true;
   2145 }
   2146 
   2147 static int
   2148 x_transpose(int c MKSH_A_UNUSED)
   2149 {
   2150 	unsigned int tmpa, tmpb;
   2151 
   2152 	/*-
   2153 	 * What transpose is meant to do seems to be up for debate. This
   2154 	 * is a general summary of the options; the text is abcd with the
   2155 	 * upper case character or underscore indicating the cursor position:
   2156 	 *	Who			Before	After	Before	After
   2157 	 *	AT&T ksh in emacs mode:	abCd	abdC	abcd_	(bell)
   2158 	 *	AT&T ksh in gmacs mode:	abCd	baCd	abcd_	abdc_
   2159 	 *	gnu emacs:		abCd	acbD	abcd_	abdc_
   2160 	 * Pdksh currently goes with GNU behavior since I believe this is the
   2161 	 * most common version of emacs, unless in gmacs mode, in which case
   2162 	 * it does the AT&T ksh gmacs mode.
   2163 	 * This should really be broken up into 3 functions so users can bind
   2164 	 * to the one they want.
   2165 	 */
   2166 	if (xcp == xbuf) {
   2167 		x_e_putc2(KSH_BEL);
   2168 		return (KSTD);
   2169 	} else if (xcp == xep || Flag(FGMACS)) {
   2170 		if (xcp - xbuf == 1) {
   2171 			x_e_putc2(KSH_BEL);
   2172 			return (KSTD);
   2173 		}
   2174 		/*
   2175 		 * Gosling/Unipress emacs style: Swap two characters before
   2176 		 * the cursor, do not change cursor position
   2177 		 */
   2178 		x_bs3(&xcp);
   2179 		if (utf_mbtowc(&tmpa, xcp) == (size_t)-1) {
   2180 			x_e_putc2(KSH_BEL);
   2181 			return (KSTD);
   2182 		}
   2183 		x_bs3(&xcp);
   2184 		if (utf_mbtowc(&tmpb, xcp) == (size_t)-1) {
   2185 			x_e_putc2(KSH_BEL);
   2186 			return (KSTD);
   2187 		}
   2188 		utf_wctomb(xcp, tmpa);
   2189 		x_zotc3(&xcp);
   2190 		utf_wctomb(xcp, tmpb);
   2191 		x_zotc3(&xcp);
   2192 	} else {
   2193 		/*
   2194 		 * GNU emacs style: Swap the characters before and under the
   2195 		 * cursor, move cursor position along one.
   2196 		 */
   2197 		if (utf_mbtowc(&tmpa, xcp) == (size_t)-1) {
   2198 			x_e_putc2(KSH_BEL);
   2199 			return (KSTD);
   2200 		}
   2201 		x_bs3(&xcp);
   2202 		if (utf_mbtowc(&tmpb, xcp) == (size_t)-1) {
   2203 			x_e_putc2(KSH_BEL);
   2204 			return (KSTD);
   2205 		}
   2206 		utf_wctomb(xcp, tmpa);
   2207 		x_zotc3(&xcp);
   2208 		utf_wctomb(xcp, tmpb);
   2209 		x_zotc3(&xcp);
   2210 	}
   2211 	x_modified();
   2212 	return (KSTD);
   2213 }
   2214 
   2215 static int
   2216 x_literal(int c MKSH_A_UNUSED)
   2217 {
   2218 	x_curprefix = -1;
   2219 	return (KSTD);
   2220 }
   2221 
   2222 static int
   2223 x_meta1(int c MKSH_A_UNUSED)
   2224 {
   2225 	x_curprefix = 1;
   2226 	return (KSTD);
   2227 }
   2228 
   2229 static int
   2230 x_meta2(int c MKSH_A_UNUSED)
   2231 {
   2232 	x_curprefix = 2;
   2233 	return (KSTD);
   2234 }
   2235 
   2236 static int
   2237 x_meta3(int c MKSH_A_UNUSED)
   2238 {
   2239 	x_curprefix = 3;
   2240 	return (KSTD);
   2241 }
   2242 
   2243 static int
   2244 x_kill(int c MKSH_A_UNUSED)
   2245 {
   2246 	size_t col = xcp - xbuf;
   2247 	size_t lastcol = xep - xbuf;
   2248 	size_t ndel, narg;
   2249 
   2250 	if (x_arg_defaulted || (narg = x_arg) > lastcol)
   2251 		narg = lastcol;
   2252 	if (narg < col) {
   2253 		x_goto(xbuf + narg);
   2254 		ndel = col - narg;
   2255 	} else
   2256 		ndel = narg - col;
   2257 	x_delete(x_nb2nc(ndel), true);
   2258 	return (KSTD);
   2259 }
   2260 
   2261 static void
   2262 x_push(size_t nchars)
   2263 {
   2264 	afree(killstack[killsp], AEDIT);
   2265 	strndupx(killstack[killsp], xcp, nchars, AEDIT);
   2266 	killsp = (killsp + 1) % KILLSIZE;
   2267 }
   2268 
   2269 static int
   2270 x_yank(int c MKSH_A_UNUSED)
   2271 {
   2272 	if (killsp == 0)
   2273 		killtp = KILLSIZE;
   2274 	else
   2275 		killtp = killsp;
   2276 	killtp--;
   2277 	if (killstack[killtp] == 0) {
   2278 		x_e_puts("\nnothing to yank");
   2279 		x_redraw('\n');
   2280 		return (KSTD);
   2281 	}
   2282 	xmp = xcp;
   2283 	x_ins(killstack[killtp]);
   2284 	return (KSTD);
   2285 }
   2286 
   2287 static int
   2288 x_meta_yank(int c MKSH_A_UNUSED)
   2289 {
   2290 	size_t len;
   2291 
   2292 	if ((x_last_command != XFUNC_yank && x_last_command != XFUNC_meta_yank) ||
   2293 	    killstack[killtp] == 0) {
   2294 		killtp = killsp;
   2295 		x_e_puts("\nyank something first");
   2296 		x_redraw('\n');
   2297 		return (KSTD);
   2298 	}
   2299 	len = strlen(killstack[killtp]);
   2300 	x_goto(xcp - len);
   2301 	x_delete(x_nb2nc(len), false);
   2302 	do {
   2303 		if (killtp == 0)
   2304 			killtp = KILLSIZE - 1;
   2305 		else
   2306 			killtp--;
   2307 	} while (killstack[killtp] == 0);
   2308 	x_ins(killstack[killtp]);
   2309 	return (KSTD);
   2310 }
   2311 
   2312 /* fake receiving an interrupt */
   2313 static void
   2314 x_intr(int signo, int c)
   2315 {
   2316 	x_vi_zotc(c);
   2317 	*xep = '\0';
   2318 	strip_nuls(xbuf, xep - xbuf);
   2319 	if (*xbuf)
   2320 		histsave(&source->line, xbuf, HIST_STORE, true);
   2321 	xlp = xep = xcp = xbp = xbuf;
   2322 	xlp_valid = true;
   2323 	*xcp = 0;
   2324 	x_modified();
   2325 	x_flush();
   2326 	trapsig(signo);
   2327 	x_mode(false);
   2328 	unwind(LSHELL);
   2329 }
   2330 
   2331 static int
   2332 x_abort(int c MKSH_A_UNUSED)
   2333 {
   2334 	return (KINTR);
   2335 }
   2336 
   2337 static int
   2338 x_error(int c MKSH_A_UNUSED)
   2339 {
   2340 	x_e_putc2(KSH_BEL);
   2341 	return (KSTD);
   2342 }
   2343 
   2344 #ifndef MKSH_SMALL
   2345 /* special VT100 style key sequence hack */
   2346 static int
   2347 x_vt_hack(int c)
   2348 {
   2349 	/* we only support PF2-'1' for now */
   2350 	if (c != (2 << 8 | '1'))
   2351 		return (x_error(c));
   2352 
   2353 	/* what's the next character? */
   2354 	switch ((c = x_e_getc())) {
   2355 	case '~':
   2356 		x_arg = 1;
   2357 		x_arg_defaulted = true;
   2358 		return (x_mv_beg(0));
   2359 	case ';':
   2360 		/* "interesting" sequence detected */
   2361 		break;
   2362 	default:
   2363 		goto unwind_err;
   2364 	}
   2365 
   2366 	/* XXX x_e_ungetc is one-octet only */
   2367 	if ((c = x_e_getc()) != '5' && c != '3')
   2368 		goto unwind_err;
   2369 
   2370 	/*-
   2371 	 * At this point, we have read the following octets so far:
   2372 	 * - ESC+[ or ESC+O or Ctrl-X (Prefix 2)
   2373 	 * - 1 (vt_hack)
   2374 	 * - ;
   2375 	 * - 5 (Ctrl key combiner) or 3 (Alt key combiner)
   2376 	 * We can now accept one more octet designating the key.
   2377 	 */
   2378 
   2379 	switch ((c = x_e_getc())) {
   2380 	case 'C':
   2381 		return (x_mv_fword(c));
   2382 	case 'D':
   2383 		return (x_mv_bword(c));
   2384 	}
   2385 
   2386  unwind_err:
   2387 	x_e_ungetc(c);
   2388 	return (x_error(c));
   2389 }
   2390 #endif
   2391 
   2392 static char *
   2393 x_mapin(const char *cp, Area *ap)
   2394 {
   2395 	char *news, *op;
   2396 
   2397 	strdupx(news, cp, ap);
   2398 	op = news;
   2399 	while (*cp) {
   2400 		switch (*cp) {
   2401 		case '^':
   2402 			cp++;
   2403 			*op++ = ksh_toctrl(*cp);
   2404 			break;
   2405 		case '\\':
   2406 			if (cp[1] == '\\' || cp[1] == '^')
   2407 				++cp;
   2408 			/* FALLTHROUGH */
   2409 		default:
   2410 			*op++ = *cp;
   2411 		}
   2412 		cp++;
   2413 	}
   2414 	*op = '\0';
   2415 
   2416 	return (news);
   2417 }
   2418 
   2419 static void
   2420 x_mapout2(int c, char **buf)
   2421 {
   2422 	char *p = *buf;
   2423 
   2424 	if (ksh_isctrl(c)) {
   2425 		*p++ = '^';
   2426 		*p++ = ksh_unctrl(c);
   2427 	} else
   2428 		*p++ = c;
   2429 	*p = 0;
   2430 	*buf = p;
   2431 }
   2432 
   2433 static char *
   2434 x_mapout(int c)
   2435 {
   2436 	static char buf[8];
   2437 	char *bp = buf;
   2438 
   2439 	x_mapout2(c, &bp);
   2440 	return (buf);
   2441 }
   2442 
   2443 static void
   2444 x_print(int prefix, int key)
   2445 {
   2446 	int f = x_tab[prefix][key];
   2447 
   2448 	if (prefix)
   2449 		/* prefix == 1 || prefix == 2 || prefix == 3 */
   2450 		shf_puts(x_mapout(prefix == 1 ? CTRL_BO :
   2451 		    prefix == 2 ? CTRL_X : 0), shl_stdout);
   2452 #ifdef MKSH_SMALL
   2453 	shprintf("%s = ", x_mapout(key));
   2454 #else
   2455 	shprintf("%s%s = ", x_mapout(key), (f & 0x80) ? "~" : "");
   2456 	if (XFUNC_VALUE(f) != XFUNC_ins_string)
   2457 #endif
   2458 		shprintf(Tf_sN, x_ftab[XFUNC_VALUE(f)].xf_name);
   2459 #ifndef MKSH_SMALL
   2460 	else
   2461 		shprintf("'%s'\n", x_atab[prefix][key]);
   2462 #endif
   2463 }
   2464 
   2465 int
   2466 x_bind(const char *a1, const char *a2,
   2467 #ifndef MKSH_SMALL
   2468     /* bind -m */
   2469     bool macro,
   2470 #endif
   2471     /* bind -l */
   2472     bool list)
   2473 {
   2474 	unsigned char f;
   2475 	int prefix, key;
   2476 	char *m1, *m2;
   2477 #ifndef MKSH_SMALL
   2478 	char *sp = NULL;
   2479 	bool hastilde;
   2480 #endif
   2481 
   2482 	if (x_tab == NULL) {
   2483 		bi_errorf("can't bind, not a tty");
   2484 		return (1);
   2485 	}
   2486 	/* List function names */
   2487 	if (list) {
   2488 		for (f = 0; f < NELEM(x_ftab); f++)
   2489 			if (!(x_ftab[f].xf_flags & XF_NOBIND))
   2490 				shprintf(Tf_sN, x_ftab[f].xf_name);
   2491 		return (0);
   2492 	}
   2493 	if (a1 == NULL) {
   2494 		for (prefix = 0; prefix < X_NTABS; prefix++)
   2495 			for (key = 0; key < X_TABSZ; key++) {
   2496 				f = XFUNC_VALUE(x_tab[prefix][key]);
   2497 				if (f == XFUNC_insert || f == XFUNC_error
   2498 #ifndef MKSH_SMALL
   2499 				    || (macro && f != XFUNC_ins_string)
   2500 #endif
   2501 				    )
   2502 					continue;
   2503 				x_print(prefix, key);
   2504 			}
   2505 		return (0);
   2506 	}
   2507 	m2 = m1 = x_mapin(a1, ATEMP);
   2508 	prefix = 0;
   2509 	for (;; m1++) {
   2510 		key = (unsigned char)*m1;
   2511 		f = XFUNC_VALUE(x_tab[prefix][key]);
   2512 		if (f == XFUNC_meta1)
   2513 			prefix = 1;
   2514 		else if (f == XFUNC_meta2)
   2515 			prefix = 2;
   2516 		else if (f == XFUNC_meta3)
   2517 			prefix = 3;
   2518 		else
   2519 			break;
   2520 	}
   2521 	if (*++m1
   2522 #ifndef MKSH_SMALL
   2523 	    && ((*m1 != '~') || *(m1 + 1))
   2524 #endif
   2525 	    ) {
   2526 		char msg[256];
   2527 		const char *c = a1;
   2528 		m1 = msg;
   2529 		while (*c && (size_t)(m1 - msg) < sizeof(msg) - 3)
   2530 			x_mapout2(*c++, &m1);
   2531 		bi_errorf("too long key sequence: %s", msg);
   2532 		return (1);
   2533 	}
   2534 #ifndef MKSH_SMALL
   2535 	hastilde = tobool(*m1);
   2536 #endif
   2537 	afree(m2, ATEMP);
   2538 
   2539 	if (a2 == NULL) {
   2540 		x_print(prefix, key);
   2541 		return (0);
   2542 	}
   2543 	if (*a2 == 0) {
   2544 		f = XFUNC_insert;
   2545 #ifndef MKSH_SMALL
   2546 	} else if (macro) {
   2547 		f = XFUNC_ins_string;
   2548 		sp = x_mapin(a2, AEDIT);
   2549 #endif
   2550 	} else {
   2551 		for (f = 0; f < NELEM(x_ftab); f++)
   2552 			if (!strcmp(x_ftab[f].xf_name, a2))
   2553 				break;
   2554 		if (f == NELEM(x_ftab) || x_ftab[f].xf_flags & XF_NOBIND) {
   2555 			bi_errorf("%s: no such function", a2);
   2556 			return (1);
   2557 		}
   2558 	}
   2559 
   2560 #ifndef MKSH_SMALL
   2561 	if (XFUNC_VALUE(x_tab[prefix][key]) == XFUNC_ins_string &&
   2562 	    x_atab[prefix][key])
   2563 		afree(x_atab[prefix][key], AEDIT);
   2564 #endif
   2565 	x_tab[prefix][key] = f
   2566 #ifndef MKSH_SMALL
   2567 	    | (hastilde ? 0x80 : 0)
   2568 #endif
   2569 	    ;
   2570 #ifndef MKSH_SMALL
   2571 	x_atab[prefix][key] = sp;
   2572 #endif
   2573 
   2574 	/* Track what the user has bound so x_mode(true) won't toast things */
   2575 	if (f == XFUNC_insert)
   2576 		x_bound[(prefix * X_TABSZ + key) / 8] &=
   2577 		    ~(1 << ((prefix * X_TABSZ + key) % 8));
   2578 	else
   2579 		x_bound[(prefix * X_TABSZ + key) / 8] |=
   2580 		    (1 << ((prefix * X_TABSZ + key) % 8));
   2581 
   2582 	return (0);
   2583 }
   2584 
   2585 static void
   2586 bind_if_not_bound(int p, int k, int func)
   2587 {
   2588 	int t;
   2589 
   2590 	/*
   2591 	 * Has user already bound this key?
   2592 	 * If so, do not override it.
   2593 	 */
   2594 	t = p * X_TABSZ + k;
   2595 	if (x_bound[t >> 3] & (1 << (t & 7)))
   2596 		return;
   2597 
   2598 	x_tab[p][k] = func;
   2599 }
   2600 
   2601 static int
   2602 x_set_mark(int c MKSH_A_UNUSED)
   2603 {
   2604 	xmp = xcp;
   2605 	return (KSTD);
   2606 }
   2607 
   2608 static int
   2609 x_kill_region(int c MKSH_A_UNUSED)
   2610 {
   2611 	size_t rsize;
   2612 	char *xr;
   2613 
   2614 	if (xmp == NULL) {
   2615 		x_e_putc2(KSH_BEL);
   2616 		return (KSTD);
   2617 	}
   2618 	if (xmp > xcp) {
   2619 		rsize = xmp - xcp;
   2620 		xr = xcp;
   2621 	} else {
   2622 		rsize = xcp - xmp;
   2623 		xr = xmp;
   2624 	}
   2625 	x_goto(xr);
   2626 	x_delete(x_nb2nc(rsize), true);
   2627 	xmp = xr;
   2628 	return (KSTD);
   2629 }
   2630 
   2631 static int
   2632 x_xchg_point_mark(int c MKSH_A_UNUSED)
   2633 {
   2634 	char *tmp;
   2635 
   2636 	if (xmp == NULL) {
   2637 		x_e_putc2(KSH_BEL);
   2638 		return (KSTD);
   2639 	}
   2640 	tmp = xmp;
   2641 	xmp = xcp;
   2642 	x_goto(tmp);
   2643 	return (KSTD);
   2644 }
   2645 
   2646 static int
   2647 x_noop(int c MKSH_A_UNUSED)
   2648 {
   2649 	return (KSTD);
   2650 }
   2651 
   2652 /*
   2653  *	File/command name completion routines
   2654  */
   2655 static int
   2656 x_comp_comm(int c MKSH_A_UNUSED)
   2657 {
   2658 	do_complete(XCF_COMMAND, CT_COMPLETE);
   2659 	return (KSTD);
   2660 }
   2661 
   2662 static int
   2663 x_list_comm(int c MKSH_A_UNUSED)
   2664 {
   2665 	do_complete(XCF_COMMAND, CT_LIST);
   2666 	return (KSTD);
   2667 }
   2668 
   2669 static int
   2670 x_complete(int c MKSH_A_UNUSED)
   2671 {
   2672 	do_complete(XCF_COMMAND_FILE, CT_COMPLETE);
   2673 	return (KSTD);
   2674 }
   2675 
   2676 static int
   2677 x_enumerate(int c MKSH_A_UNUSED)
   2678 {
   2679 	do_complete(XCF_COMMAND_FILE, CT_LIST);
   2680 	return (KSTD);
   2681 }
   2682 
   2683 static int
   2684 x_comp_file(int c MKSH_A_UNUSED)
   2685 {
   2686 	do_complete(XCF_FILE, CT_COMPLETE);
   2687 	return (KSTD);
   2688 }
   2689 
   2690 static int
   2691 x_list_file(int c MKSH_A_UNUSED)
   2692 {
   2693 	do_complete(XCF_FILE, CT_LIST);
   2694 	return (KSTD);
   2695 }
   2696 
   2697 static int
   2698 x_comp_list(int c MKSH_A_UNUSED)
   2699 {
   2700 	do_complete(XCF_COMMAND_FILE, CT_COMPLIST);
   2701 	return (KSTD);
   2702 }
   2703 
   2704 static int
   2705 x_expand(int c MKSH_A_UNUSED)
   2706 {
   2707 	char **words;
   2708 	int start, end, nwords, i;
   2709 
   2710 	i = XCF_FILE;
   2711 	nwords = x_cf_glob(&i, xbuf, xep - xbuf, xcp - xbuf,
   2712 	    &start, &end, &words);
   2713 
   2714 	if (nwords == 0) {
   2715 		x_e_putc2(KSH_BEL);
   2716 		return (KSTD);
   2717 	}
   2718 	x_goto(xbuf + start);
   2719 	x_delete(x_nb2nc(end - start), false);
   2720 
   2721 	i = 0;
   2722 	while (i < nwords) {
   2723 		if (x_escape(words[i], strlen(words[i]), x_do_ins) < 0 ||
   2724 		    (++i < nwords && x_ins(T1space) < 0)) {
   2725 			x_e_putc2(KSH_BEL);
   2726 			return (KSTD);
   2727 		}
   2728 	}
   2729 	x_adjust();
   2730 
   2731 	return (KSTD);
   2732 }
   2733 
   2734 static void
   2735 do_complete(
   2736     /* XCF_{COMMAND,FILE,COMMAND_FILE} */
   2737     int flags,
   2738     /* 0 for list, 1 for complete and 2 for complete-list */
   2739     Comp_type type)
   2740 {
   2741 	char **words;
   2742 	int start, end, nlen, olen, nwords;
   2743 	bool completed;
   2744 
   2745 	nwords = x_cf_glob(&flags, xbuf, xep - xbuf, xcp - xbuf,
   2746 	    &start, &end, &words);
   2747 	/* no match */
   2748 	if (nwords == 0) {
   2749 		x_e_putc2(KSH_BEL);
   2750 		return;
   2751 	}
   2752 	if (type == CT_LIST) {
   2753 		x_print_expansions(nwords, words,
   2754 		    tobool(flags & XCF_IS_COMMAND));
   2755 		x_redraw(0);
   2756 		x_free_words(nwords, words);
   2757 		return;
   2758 	}
   2759 	olen = end - start;
   2760 	nlen = x_longest_prefix(nwords, words);
   2761 	if (nwords == 1) {
   2762 		/*
   2763 		 * always complete single matches;
   2764 		 * any expansion of parameter substitution
   2765 		 * is always at most one result, too
   2766 		 */
   2767 		completed = true;
   2768 	} else {
   2769 		char *unescaped;
   2770 
   2771 		/* make a copy of the original string part */
   2772 		strndupx(unescaped, xbuf + start, olen, ATEMP);
   2773 
   2774 		/* expand any tilde and unescape the string for comparison */
   2775 		unescaped = x_glob_hlp_tilde_and_rem_qchar(unescaped, true);
   2776 
   2777 		/*
   2778 		 * match iff entire original string is part of the
   2779 		 * longest prefix, implying the latter is at least
   2780 		 * the same size (after unescaping)
   2781 		 */
   2782 		completed = !strncmp(words[0], unescaped, strlen(unescaped));
   2783 
   2784 		afree(unescaped, ATEMP);
   2785 	}
   2786 	if (type == CT_COMPLIST && nwords > 1) {
   2787 		/*
   2788 		 * print expansions, since we didn't get back
   2789 		 * just a single match
   2790 		 */
   2791 		x_print_expansions(nwords, words,
   2792 		    tobool(flags & XCF_IS_COMMAND));
   2793 	}
   2794 	if (completed) {
   2795 		/* expand on the command line */
   2796 		xmp = NULL;
   2797 		xcp = xbuf + start;
   2798 		xep -= olen;
   2799 		memmove(xcp, xcp + olen, xep - xcp + 1);
   2800 		x_escape(words[0], nlen, x_do_ins);
   2801 	}
   2802 	x_adjust();
   2803 	/*
   2804 	 * append a space if this is a single non-directory match
   2805 	 * and not a parameter or homedir substitution
   2806 	 */
   2807 	if (nwords == 1 && !mksh_cdirsep(words[0][nlen - 1]) &&
   2808 	    !(flags & XCF_IS_NOSPACE)) {
   2809 		x_ins(T1space);
   2810 	}
   2811 
   2812 	x_free_words(nwords, words);
   2813 }
   2814 
   2815 /*-
   2816  * NAME:
   2817  *	x_adjust - redraw the line adjusting starting point etc.
   2818  *
   2819  * DESCRIPTION:
   2820  *	This function is called when we have exceeded the bounds
   2821  *	of the edit window. It increments x_adj_done so that
   2822  *	functions like x_ins and x_delete know that we have been
   2823  *	called and can skip the x_bs() stuff which has already
   2824  *	been done by x_redraw.
   2825  *
   2826  * RETURN VALUE:
   2827  *	None
   2828  */
   2829 static void
   2830 x_adjust(void)
   2831 {
   2832 	int col_left, n;
   2833 
   2834 	/* flag the fact that we were called */
   2835 	x_adj_done++;
   2836 
   2837 	/*
   2838 	 * calculate the amount of columns we need to "go back"
   2839 	 * from xcp to set xbp to (but never < xbuf) to 2/3 of
   2840 	 * the display width; take care of pwidth though
   2841 	 */
   2842 	if ((col_left = xx_cols * 2 / 3) < MIN_EDIT_SPACE) {
   2843 		/*
   2844 		 * cowardly refuse to do anything
   2845 		 * if the available space is too small;
   2846 		 * fall back to dumb pdksh code
   2847 		 */
   2848 		if ((xbp = xcp - (x_displen / 2)) < xbuf)
   2849 			xbp = xbuf;
   2850 		/* elide UTF-8 fixup as penalty */
   2851 		goto x_adjust_out;
   2852 	}
   2853 
   2854 	/* fix up xbp to just past a character end first */
   2855 	xbp = xcp >= xep ? xep : x_bs0(xcp, xbuf);
   2856 	/* walk backwards */
   2857 	while (xbp > xbuf && col_left > 0) {
   2858 		xbp = x_bs0(xbp - 1, xbuf);
   2859 		col_left -= (n = x_size2(xbp, NULL));
   2860 	}
   2861 	/* check if we hit the prompt */
   2862 	if (xbp == xbuf && xcp != xbuf && col_left >= 0 && col_left < pwidth) {
   2863 		/* so we did; force scrolling occurs */
   2864 		xbp += utf_ptradj(xbp);
   2865 	}
   2866 
   2867  x_adjust_out:
   2868 	xlp_valid = false;
   2869 	x_redraw('\r');
   2870 	x_flush();
   2871 }
   2872 
   2873 static void
   2874 x_e_ungetc(int c)
   2875 {
   2876 	unget_char = c < 0 ? -1 : (c & 255);
   2877 }
   2878 
   2879 static int
   2880 x_e_getc(void)
   2881 {
   2882 	int c;
   2883 
   2884 	if (unget_char >= 0) {
   2885 		c = unget_char;
   2886 		unget_char = -1;
   2887 		return (c);
   2888 	}
   2889 
   2890 #ifndef MKSH_SMALL
   2891 	if (macroptr) {
   2892 		if ((c = (unsigned char)*macroptr++))
   2893 			return (c);
   2894 		macroptr = NULL;
   2895 	}
   2896 #endif
   2897 
   2898 	return (x_getc());
   2899 }
   2900 
   2901 static void
   2902 x_e_putc2(int c)
   2903 {
   2904 	int width = 1;
   2905 
   2906 	if (ctype(c, C_CR | C_LF))
   2907 		x_col = 0;
   2908 	if (x_col < xx_cols) {
   2909 #ifndef MKSH_EBCDIC
   2910 		if (UTFMODE && (c > 0x7F)) {
   2911 			char utf_tmp[3];
   2912 			size_t x;
   2913 
   2914 			if (c < 0xA0)
   2915 				c = 0xFFFD;
   2916 			x = utf_wctomb(utf_tmp, c);
   2917 			x_putc(utf_tmp[0]);
   2918 			if (x > 1)
   2919 				x_putc(utf_tmp[1]);
   2920 			if (x > 2)
   2921 				x_putc(utf_tmp[2]);
   2922 			width = utf_wcwidth(c);
   2923 		} else
   2924 #endif
   2925 			x_putc(c);
   2926 		switch (c) {
   2927 		case KSH_BEL:
   2928 			break;
   2929 		case '\r':
   2930 		case '\n':
   2931 			break;
   2932 		case '\b':
   2933 			x_col--;
   2934 			break;
   2935 		default:
   2936 			x_col += width;
   2937 			break;
   2938 		}
   2939 	}
   2940 	if (x_adj_ok && (x_col < 0 || x_col >= (xx_cols - 2)))
   2941 		x_adjust();
   2942 }
   2943 
   2944 static void
   2945 x_e_putc3(const char **cp)
   2946 {
   2947 	int width = 1, c = **(const unsigned char **)cp;
   2948 
   2949 	if (ctype(c, C_CR | C_LF))
   2950 		x_col = 0;
   2951 	if (x_col < xx_cols) {
   2952 		if (UTFMODE && (c > 0x7F)) {
   2953 			char *cp2;
   2954 
   2955 			width = utf_widthadj(*cp, (const char **)&cp2);
   2956 			if (cp2 == *cp + 1) {
   2957 				(*cp)++;
   2958 #ifdef MKSH_EBCDIC
   2959 				x_putc(asc2rtt(0xEF));
   2960 				x_putc(asc2rtt(0xBF));
   2961 				x_putc(asc2rtt(0xBD));
   2962 #else
   2963 				shf_puts("\xEF\xBF\xBD", shl_out);
   2964 #endif
   2965 			} else
   2966 				while (*cp < cp2)
   2967 					x_putcf(*(*cp)++);
   2968 		} else {
   2969 			(*cp)++;
   2970 			x_putc(c);
   2971 		}
   2972 		switch (c) {
   2973 		case KSH_BEL:
   2974 			break;
   2975 		case '\r':
   2976 		case '\n':
   2977 			break;
   2978 		case '\b':
   2979 			x_col--;
   2980 			break;
   2981 		default:
   2982 			x_col += width;
   2983 			break;
   2984 		}
   2985 	}
   2986 	if (x_adj_ok && (x_col < 0 || x_col >= (xx_cols - 2)))
   2987 		x_adjust();
   2988 }
   2989 
   2990 static void
   2991 x_e_puts(const char *s)
   2992 {
   2993 	int adj = x_adj_done;
   2994 
   2995 	while (*s && adj == x_adj_done)
   2996 		x_e_putc3(&s);
   2997 }
   2998 
   2999 /*-
   3000  * NAME:
   3001  *	x_set_arg - set an arg value for next function
   3002  *
   3003  * DESCRIPTION:
   3004  *	This is a simple implementation of M-[0-9].
   3005  *
   3006  * RETURN VALUE:
   3007  *	KSTD
   3008  */
   3009 static int
   3010 x_set_arg(int c)
   3011 {
   3012 	unsigned int n = 0;
   3013 	bool first = true;
   3014 
   3015 	/* strip command prefix */
   3016 	c &= 255;
   3017 	while (c >= 0 && ctype(c, C_DIGIT)) {
   3018 		n = n * 10 + ksh_numdig(c);
   3019 		if (n > LINE)
   3020 			/* upper bound for repeat */
   3021 			goto x_set_arg_too_big;
   3022 		c = x_e_getc();
   3023 		first = false;
   3024 	}
   3025 	if (c < 0 || first) {
   3026  x_set_arg_too_big:
   3027 		x_e_putc2(KSH_BEL);
   3028 		x_arg = 1;
   3029 		x_arg_defaulted = true;
   3030 	} else {
   3031 		x_e_ungetc(c);
   3032 		x_arg = n;
   3033 		x_arg_defaulted = false;
   3034 	}
   3035 	return (KSTD);
   3036 }
   3037 
   3038 /* Comment or uncomment the current line. */
   3039 static int
   3040 x_comment(int c MKSH_A_UNUSED)
   3041 {
   3042 	ssize_t len = xep - xbuf;
   3043 	int ret = x_do_comment(xbuf, xend - xbuf, &len);
   3044 
   3045 	if (ret < 0)
   3046 		x_e_putc2(KSH_BEL);
   3047 	else {
   3048 		x_modified();
   3049 		xep = xbuf + len;
   3050 		*xep = '\0';
   3051 		xcp = xbp = xbuf;
   3052 		x_redraw('\r');
   3053 		if (ret > 0)
   3054 			return (x_newline('\n'));
   3055 	}
   3056 	return (KSTD);
   3057 }
   3058 
   3059 static int
   3060 x_version(int c MKSH_A_UNUSED)
   3061 {
   3062 	char *o_xbuf = xbuf, *o_xend = xend;
   3063 	char *o_xbp = xbp, *o_xep = xep, *o_xcp = xcp;
   3064 	char *v;
   3065 
   3066 	strdupx(v, KSH_VERSION, ATEMP);
   3067 
   3068 	xbuf = xbp = xcp = v;
   3069 	xend = xep = strnul(v);
   3070 	x_redraw('\r');
   3071 	x_flush();
   3072 
   3073 	c = x_e_getc();
   3074 	xbuf = o_xbuf;
   3075 	xend = o_xend;
   3076 	xbp = o_xbp;
   3077 	xep = o_xep;
   3078 	xcp = o_xcp;
   3079 	x_redraw('\r');
   3080 
   3081 	if (c < 0)
   3082 		return (KSTD);
   3083 	/* This is what AT&T ksh seems to do... Very bizarre */
   3084 	if (c != ' ')
   3085 		x_e_ungetc(c);
   3086 
   3087 	afree(v, ATEMP);
   3088 	return (KSTD);
   3089 }
   3090 
   3091 #ifndef MKSH_SMALL
   3092 static int
   3093 x_edit_line(int c MKSH_A_UNUSED)
   3094 {
   3095 	if (x_arg_defaulted) {
   3096 		if (xep == xbuf) {
   3097 			x_e_putc2(KSH_BEL);
   3098 			return (KSTD);
   3099 		}
   3100 		if (modified) {
   3101 			*xep = '\0';
   3102 			histsave(&source->line, xbuf, HIST_STORE, true);
   3103 			x_arg = 0;
   3104 		} else
   3105 			x_arg = source->line - (histptr - x_histp);
   3106 	}
   3107 	if (x_arg)
   3108 		shf_snprintf(xbuf, xend - xbuf, Tf_sd,
   3109 		    "fc -e ${VISUAL:-${EDITOR:-vi}} --", x_arg);
   3110 	else
   3111 		strlcpy(xbuf, "fc -e ${VISUAL:-${EDITOR:-vi}} --", xend - xbuf);
   3112 	xep = strnul(xbuf);
   3113 	return (x_newline('\n'));
   3114 }
   3115 #endif
   3116 
   3117 /*-
   3118  * NAME:
   3119  *	x_prev_histword - recover word from prev command
   3120  *
   3121  * DESCRIPTION:
   3122  *	This function recovers the last word from the previous
   3123  *	command and inserts it into the current edit line. If a
   3124  *	numeric arg is supplied then the n'th word from the
   3125  *	start of the previous command is used.
   3126  *	As a side effect, trashes the mark in order to achieve
   3127  *	being called in a repeatable fashion.
   3128  *
   3129  *	Bound to M-.
   3130  *
   3131  * RETURN VALUE:
   3132  *	KSTD
   3133  */
   3134 static int
   3135 x_prev_histword(int c MKSH_A_UNUSED)
   3136 {
   3137 	char *rcp, *cp;
   3138 	char **xhp;
   3139 	int m = 1;
   3140 	/* -1 = defaulted; 0+ = argument */
   3141 	static int last_arg = -1;
   3142 
   3143 	if (x_last_command == XFUNC_prev_histword) {
   3144 		if (xmp && modified > 1)
   3145 			x_kill_region(0);
   3146 		if (modified)
   3147 			m = modified;
   3148 	} else
   3149 		last_arg = x_arg_defaulted ? -1 : x_arg;
   3150 	xhp = histptr - (m - 1);
   3151 	if ((xhp < history) || !(cp = *xhp)) {
   3152 		x_e_putc2(KSH_BEL);
   3153 		x_modified();
   3154 		return (KSTD);
   3155 	}
   3156 	x_set_mark(0);
   3157 	if ((x_arg = last_arg) == -1) {
   3158 		/* x_arg_defaulted */
   3159 
   3160 		rcp = &cp[strlen(cp) - 1];
   3161 		/*
   3162 		 * ignore white-space after the last word
   3163 		 */
   3164 		while (rcp > cp && ctype(*rcp, C_CFS))
   3165 			rcp--;
   3166 		while (rcp > cp && !ctype(*rcp, C_CFS))
   3167 			rcp--;
   3168 		if (ctype(*rcp, C_CFS))
   3169 			rcp++;
   3170 		x_ins(rcp);
   3171 	} else {
   3172 		/* not x_arg_defaulted */
   3173 		char ch;
   3174 
   3175 		rcp = cp;
   3176 		/*
   3177 		 * ignore white-space at start of line
   3178 		 */
   3179 		while (*rcp && ctype(*rcp, C_CFS))
   3180 			rcp++;
   3181 		while (x_arg-- > 0) {
   3182 			while (*rcp && !ctype(*rcp, C_CFS))
   3183 				rcp++;
   3184 			while (*rcp && ctype(*rcp, C_CFS))
   3185 				rcp++;
   3186 		}
   3187 		cp = rcp;
   3188 		while (*rcp && !ctype(*rcp, C_CFS))
   3189 			rcp++;
   3190 		ch = *rcp;
   3191 		*rcp = '\0';
   3192 		x_ins(cp);
   3193 		*rcp = ch;
   3194 	}
   3195 	if (!modified)
   3196 		x_histmcp = x_histp;
   3197 	modified = m + 1;
   3198 	return (KSTD);
   3199 }
   3200 
   3201 #ifndef MKSH_SMALL
   3202 /* Uppercase N(1) words */
   3203 static int
   3204 x_fold_upper(int c MKSH_A_UNUSED)
   3205 {
   3206 	return (x_fold_case('U'));
   3207 }
   3208 
   3209 /* Lowercase N(1) words */
   3210 static int
   3211 x_fold_lower(int c MKSH_A_UNUSED)
   3212 {
   3213 	return (x_fold_case('L'));
   3214 }
   3215 
   3216 /* Titlecase N(1) words */
   3217 static int
   3218 x_fold_capitalise(int c MKSH_A_UNUSED)
   3219 {
   3220 	return (x_fold_case('C'));
   3221 }
   3222 
   3223 /*-
   3224  * NAME:
   3225  *	x_fold_case - convert word to UPPER/lower/Capital case
   3226  *
   3227  * DESCRIPTION:
   3228  *	This function is used to implement M-U/M-u, M-L/M-l, M-C/M-c
   3229  *	to UPPER CASE, lower case or Capitalise Words.
   3230  *
   3231  * RETURN VALUE:
   3232  *	None
   3233  */
   3234 static int
   3235 x_fold_case(int c)
   3236 {
   3237 	char *cp = xcp;
   3238 
   3239 	if (cp == xep) {
   3240 		x_e_putc2(KSH_BEL);
   3241 		return (KSTD);
   3242 	}
   3243 	while (x_arg--) {
   3244 		/*
   3245 		 * first skip over any white-space
   3246 		 */
   3247 		while (cp != xep && ctype(*cp, C_MFS))
   3248 			cp++;
   3249 		/*
   3250 		 * do the first char on its own since it may be
   3251 		 * a different action than for the rest.
   3252 		 */
   3253 		if (cp != xep) {
   3254 			if (c == 'L')
   3255 				/* lowercase */
   3256 				*cp = ksh_tolower(*cp);
   3257 			else
   3258 				/* uppercase, capitalise */
   3259 				*cp = ksh_toupper(*cp);
   3260 			cp++;
   3261 		}
   3262 		/*
   3263 		 * now for the rest of the word
   3264 		 */
   3265 		while (cp != xep && !ctype(*cp, C_MFS)) {
   3266 			if (c == 'U')
   3267 				/* uppercase */
   3268 				*cp = ksh_toupper(*cp);
   3269 			else
   3270 				/* lowercase, capitalise */
   3271 				*cp = ksh_tolower(*cp);
   3272 			cp++;
   3273 		}
   3274 	}
   3275 	x_goto(cp);
   3276 	x_modified();
   3277 	return (KSTD);
   3278 }
   3279 #endif
   3280 
   3281 /*-
   3282  * NAME:
   3283  *	x_lastcp - last visible char
   3284  *
   3285  * DESCRIPTION:
   3286  *	This function returns a pointer to that char in the
   3287  *	edit buffer that will be the last displayed on the
   3288  *	screen.
   3289  */
   3290 static char *
   3291 x_lastcp(void)
   3292 {
   3293 	if (!xlp_valid) {
   3294 		int i = 0, j;
   3295 		char *xlp2;
   3296 
   3297 		xlp = xbp;
   3298 		while (xlp < xep) {
   3299 			j = x_size2(xlp, &xlp2);
   3300 			if ((i + j) > x_displen)
   3301 				break;
   3302 			i += j;
   3303 			xlp = xlp2;
   3304 		}
   3305 	}
   3306 	xlp_valid = true;
   3307 	return (xlp);
   3308 }
   3309 
   3310 /* correctly position the cursor on the screen from end of visible area */
   3311 static void
   3312 x_lastpos(void)
   3313 {
   3314 	char *cp = x_lastcp();
   3315 
   3316 	while (cp > xcp)
   3317 		x_bs3(&cp);
   3318 }
   3319 
   3320 static void
   3321 x_mode(bool onoff)
   3322 {
   3323 	static bool x_cur_mode;
   3324 
   3325 	if (x_cur_mode == onoff)
   3326 		return;
   3327 	x_cur_mode = onoff;
   3328 
   3329 	if (onoff) {
   3330 		x_mkraw(tty_fd, NULL, false);
   3331 
   3332 		edchars.erase = toedchar(tty_state.c_cc[VERASE]);
   3333 		edchars.kill = toedchar(tty_state.c_cc[VKILL]);
   3334 		edchars.intr = toedchar(tty_state.c_cc[VINTR]);
   3335 		edchars.quit = toedchar(tty_state.c_cc[VQUIT]);
   3336 		edchars.eof = toedchar(tty_state.c_cc[VEOF]);
   3337 #ifdef VWERASE
   3338 		edchars.werase = toedchar(tty_state.c_cc[VWERASE]);
   3339 #else
   3340 		edchars.werase = 0;
   3341 #endif
   3342 
   3343 		if (!edchars.erase)
   3344 			edchars.erase = CTRL_H;
   3345 		if (!edchars.kill)
   3346 			edchars.kill = CTRL_U;
   3347 		if (!edchars.intr)
   3348 			edchars.intr = CTRL_C;
   3349 		if (!edchars.quit)
   3350 			edchars.quit = CTRL_BK;
   3351 		if (!edchars.eof)
   3352 			edchars.eof = CTRL_D;
   3353 		if (!edchars.werase)
   3354 			edchars.werase = CTRL_W;
   3355 
   3356 		if (isedchar(edchars.erase)) {
   3357 			bind_if_not_bound(0, edchars.erase, XFUNC_del_back);
   3358 			bind_if_not_bound(1, edchars.erase, XFUNC_del_bword);
   3359 		}
   3360 		if (isedchar(edchars.kill))
   3361 			bind_if_not_bound(0, edchars.kill, XFUNC_del_line);
   3362 		if (isedchar(edchars.werase))
   3363 			bind_if_not_bound(0, edchars.werase, XFUNC_del_bword);
   3364 		if (isedchar(edchars.intr))
   3365 			bind_if_not_bound(0, edchars.intr, XFUNC_abort);
   3366 		if (isedchar(edchars.quit))
   3367 			bind_if_not_bound(0, edchars.quit, XFUNC_noop);
   3368 	} else
   3369 		mksh_tcset(tty_fd, &tty_state);
   3370 }
   3371 
   3372 #if !MKSH_S_NOVI
   3373 /* +++ vi editing mode +++ */
   3374 
   3375 struct edstate {
   3376 	char *cbuf;
   3377 	ssize_t winleft;
   3378 	ssize_t cbufsize;
   3379 	ssize_t linelen;
   3380 	ssize_t cursor;
   3381 };
   3382 
   3383 static int vi_hook(int);
   3384 static int nextstate(int);
   3385 static int vi_insert(int);
   3386 static int vi_cmd(int, const char *);
   3387 static int domove(int, const char *, int);
   3388 static int domovebeg(void);
   3389 static int redo_insert(int);
   3390 static void yank_range(int, int);
   3391 static int bracktype(int);
   3392 static void save_cbuf(void);
   3393 static void restore_cbuf(void);
   3394 static int putbuf(const char *, ssize_t, bool);
   3395 static void del_range(int, int);
   3396 static int findch(int, int, bool, bool) MKSH_A_PURE;
   3397 static int forwword(int);
   3398 static int backword(int);
   3399 static int endword(int);
   3400 static int Forwword(int);
   3401 static int Backword(int);
   3402 static int Endword(int);
   3403 static int grabhist(int, int);
   3404 static int grabsearch(int, int, int, const char *);
   3405 static void redraw_line(bool);
   3406 static void refresh(int);
   3407 static int outofwin(void);
   3408 static void rewindow(void);
   3409 static int newcol(unsigned char, int);
   3410 static void display(char *, char *, int);
   3411 static void ed_mov_opt(int, char *);
   3412 static int expand_word(int);
   3413 static int complete_word(int, int);
   3414 static int print_expansions(struct edstate *, int);
   3415 static void vi_error(void);
   3416 static void vi_macro_reset(void);
   3417 static int x_vi_putbuf(const char *, size_t);
   3418 #define char_len(c) (ksh_isctrl(c) ? 2 : 1)
   3419 
   3420 #define vC	0x01		/* a valid command that isn't a vM, vE, vU */
   3421 #define vM	0x02		/* movement command (h, l, etc.) */
   3422 #define vE	0x04		/* extended command (c, d, y) */
   3423 #define vX	0x08		/* long command (@, f, F, t, T, etc.) */
   3424 #define vU	0x10		/* an UN-undoable command (that isn't a vM) */
   3425 #define vB	0x20		/* bad command (^@) */
   3426 #define vZ	0x40		/* repeat count defaults to 0 (not 1) */
   3427 #define vS	0x80		/* search (/, ?) */
   3428 
   3429 #define is_bad(c)	(classify[rtt2asc(c) & 0x7F] & vB)
   3430 #define is_cmd(c)	(classify[rtt2asc(c) & 0x7F] & (vM | vE | vC | vU))
   3431 #define is_move(c)	(classify[rtt2asc(c) & 0x7F] & vM)
   3432 #define is_extend(c)	(classify[rtt2asc(c) & 0x7F] & vE)
   3433 #define is_long(c)	(classify[rtt2asc(c) & 0x7F] & vX)
   3434 #define is_undoable(c)	(!(classify[rtt2asc(c) & 0x7F] & vU))
   3435 #define is_srch(c)	(classify[rtt2asc(c) & 0x7F] & vS)
   3436 #define is_zerocount(c)	(classify[rtt2asc(c) & 0x7F] & vZ)
   3437 
   3438 static const unsigned char classify[128] = {
   3439 /*	 0	1	2	3	4	5	6	7	*/
   3440 /* 0	^@	^A	^B	^C	^D	^E	^F	^G	*/
   3441 	vB,	0,	0,	0,	0,	vC|vU,	vC|vZ,	0,
   3442 /* 1	^H	^I	^J	^K	^L	^M	^N	^O	*/
   3443 	vM,	vC|vZ,	0,	0,	vC|vU,	0,	vC,	0,
   3444 /* 2	^P	^Q	^R	^S	^T	^U	^V	^W	*/
   3445 	vC,	0,	vC|vU,	0,	0,	0,	vC,	0,
   3446 /* 3	^X	^Y	^Z	^[	^\	^]	^^	^_	*/
   3447 	vC,	0,	0,	vC|vZ,	0,	0,	0,	0,
   3448 /* 4	<space>	!	"	#	$	%	&	'	*/
   3449 	vM,	0,	0,	vC,	vM,	vM,	0,	0,
   3450 /* 5	(	)	*	+	,	-	.	/	*/
   3451 	0,	0,	vC,	vC,	vM,	vC,	0,	vC|vS,
   3452 /* 6	0	1	2	3	4	5	6	7	*/
   3453 	vM,	0,	0,	0,	0,	0,	0,	0,
   3454 /* 7	8	9	:	;	<	=	>	?	*/
   3455 	0,	0,	0,	vM,	0,	vC,	0,	vC|vS,
   3456 /* 8	@	A	B	C	D	E	F	G	*/
   3457 	vC|vX,	vC,	vM,	vC,	vC,	vM,	vM|vX,	vC|vU|vZ,
   3458 /* 9	H	I	J	K	L	M	N	O	*/
   3459 	0,	vC,	0,	0,	0,	0,	vC|vU,	vU,
   3460 /* A	P	Q	R	S	T	U	V	W	*/
   3461 	vC,	0,	vC,	vC,	vM|vX,	vC,	0,	vM,
   3462 /* B	X	Y	Z	[	\	]	^	_	*/
   3463 	vC,	vC|vU,	0,	vU,	vC|vZ,	0,	vM,	vC|vZ,
   3464 /* C	`	a	b	c	d	e	f	g	*/
   3465 	0,	vC,	vM,	vE,	vE,	vM,	vM|vX,	vC|vZ,
   3466 /* D	h	i	j	k	l	m	n	o	*/
   3467 	vM,	vC,	vC|vU,	vC|vU,	vM,	0,	vC|vU,	0,
   3468 /* E	p	q	r	s	t	u	v	w	*/
   3469 	vC,	0,	vX,	vC,	vM|vX,	vC|vU,	vC|vU|vZ, vM,
   3470 /* F	x	y	z	{	|	}	~	^?	*/
   3471 	vC,	vE|vU,	0,	0,	vM|vZ,	0,	vC,	0
   3472 };
   3473 
   3474 #define MAXVICMD	3
   3475 #define SRCHLEN		40
   3476 
   3477 #define INSERT		1
   3478 #define REPLACE		2
   3479 
   3480 #define VNORMAL		0		/* command, insert or replace mode */
   3481 #define VARG1		1		/* digit prefix (first, eg, 5l) */
   3482 #define VEXTCMD		2		/* cmd + movement (eg, cl) */
   3483 #define VARG2		3		/* digit prefix (second, eg, 2c3l) */
   3484 #define VXCH		4		/* f, F, t, T, @ */
   3485 #define VFAIL		5		/* bad command */
   3486 #define VCMD		6		/* single char command (eg, X) */
   3487 #define VREDO		7		/* . */
   3488 #define VLIT		8		/* ^V */
   3489 #define VSEARCH		9		/* /, ? */
   3490 #define VVERSION	10		/* <ESC> ^V */
   3491 #define VPREFIX2	11		/* ^[[ and ^[O in insert mode */
   3492 
   3493 static struct edstate	*save_edstate(struct edstate *old);
   3494 static void		restore_edstate(struct edstate *old, struct edstate *news);
   3495 static void		free_edstate(struct edstate *old);
   3496 
   3497 static struct edstate	ebuf;
   3498 static struct edstate	undobuf;
   3499 
   3500 static struct edstate	*vs;		/* current Vi editing mode state */
   3501 static struct edstate	*undo;
   3502 
   3503 static char *ibuf;			/* input buffer */
   3504 static bool first_insert;		/* set when starting in insert mode */
   3505 static int saved_inslen;		/* saved inslen for first insert */
   3506 static int inslen;			/* length of input buffer */
   3507 static int srchlen;			/* length of current search pattern */
   3508 static char *ybuf;			/* yank buffer */
   3509 static int yanklen;			/* length of yank buffer */
   3510 static int fsavecmd = ' ';		/* last find command */
   3511 static int fsavech;			/* character to find */
   3512 static char lastcmd[MAXVICMD];		/* last non-move command */
   3513 static int lastac;			/* argcnt for lastcmd */
   3514 static int lastsearch = ' ';		/* last search command */
   3515 static char srchpat[SRCHLEN];		/* last search pattern */
   3516 static int insert;			/* <>0 in insert mode */
   3517 static int hnum;			/* position in history */
   3518 static int ohnum;			/* history line copied (after mod) */
   3519 static int hlast;			/* 1 past last position in history */
   3520 static int state;
   3521 
   3522 /*
   3523  * Information for keeping track of macros that are being expanded.
   3524  * The format of buf is the alias contents followed by a NUL byte followed
   3525  * by the name (letter) of the alias. The end of the buffer is marked by
   3526  * a double NUL. The name of the alias is stored so recursive macros can
   3527  * be detected.
   3528  */
   3529 struct macro_state {
   3530 	unsigned char *p;	/* current position in buf */
   3531 	unsigned char *buf;	/* pointer to macro(s) being expanded */
   3532 	size_t len;		/* how much data in buffer */
   3533 };
   3534 static struct macro_state macro;
   3535 
   3536 /* last input was expanded */
   3537 static enum expand_mode {
   3538 	NONE = 0, EXPAND, COMPLETE, PRINT
   3539 } expanded;
   3540 
   3541 static int
   3542 x_vi(char *buf)
   3543 {
   3544 	int c;
   3545 
   3546 	state = VNORMAL;
   3547 	ohnum = hnum = hlast = histnum(-1) + 1;
   3548 	insert = INSERT;
   3549 	saved_inslen = inslen;
   3550 	first_insert = true;
   3551 	inslen = 0;
   3552 	vi_macro_reset();
   3553 
   3554 	ebuf.cbuf = buf;
   3555 	if (undobuf.cbuf == NULL) {
   3556 		ibuf = alloc(LINE, AEDIT);
   3557 		ybuf = alloc(LINE, AEDIT);
   3558 		undobuf.cbuf = alloc(LINE, AEDIT);
   3559 	}
   3560 	undobuf.cbufsize = ebuf.cbufsize = LINE;
   3561 	undobuf.linelen = ebuf.linelen = 0;
   3562 	undobuf.cursor = ebuf.cursor = 0;
   3563 	undobuf.winleft = ebuf.winleft = 0;
   3564 	vs = &ebuf;
   3565 	undo = &undobuf;
   3566 
   3567 	x_init_prompt(true);
   3568 	x_col = pwidth;
   3569 
   3570 	if (wbuf_len != x_cols - 3 && ((wbuf_len = x_cols - 3))) {
   3571 		wbuf[0] = aresize(wbuf[0], wbuf_len, AEDIT);
   3572 		wbuf[1] = aresize(wbuf[1], wbuf_len, AEDIT);
   3573 	}
   3574 	if (wbuf_len) {
   3575 		memset(wbuf[0], ' ', wbuf_len);
   3576 		memset(wbuf[1], ' ', wbuf_len);
   3577 	}
   3578 	winwidth = x_cols - pwidth - 3;
   3579 	win = 0;
   3580 	morec = ' ';
   3581 	lastref = 1;
   3582 	holdlen = 0;
   3583 
   3584 	editmode = 2;
   3585 	x_flush();
   3586 	while (/* CONSTCOND */ 1) {
   3587 		if (macro.p) {
   3588 			c = (unsigned char)*macro.p++;
   3589 			/* end of current macro? */
   3590 			if (!c) {
   3591 				/* more macros left to finish? */
   3592 				if (*macro.p++)
   3593 					continue;
   3594 				/* must be the end of all the macros */
   3595 				vi_macro_reset();
   3596 				c = x_getc();
   3597 			}
   3598 		} else
   3599 			c = x_getc();
   3600 
   3601 		if (c == -1)
   3602 			break;
   3603 		if (state != VLIT) {
   3604 			if (isched(c, edchars.intr) ||
   3605 			    isched(c, edchars.quit)) {
   3606 				/* shove input buffer away */
   3607 				xbuf = ebuf.cbuf;
   3608 				xep = xbuf;
   3609 				if (ebuf.linelen > 0)
   3610 					xep += ebuf.linelen;
   3611 				/* pretend we got an interrupt */
   3612 				x_intr(isched(c, edchars.intr) ?
   3613 				    SIGINT : SIGQUIT, c);
   3614 			} else if (isched(c, edchars.eof) &&
   3615 			    state != VVERSION) {
   3616 				if (vs->linelen == 0) {
   3617 					x_vi_zotc(c);
   3618 					c = -1;
   3619 					break;
   3620 				}
   3621 				continue;
   3622 			}
   3623 		}
   3624 		if (vi_hook(c))
   3625 			break;
   3626 		x_flush();
   3627 	}
   3628 
   3629 	x_putc('\r');
   3630 	x_putc('\n');
   3631 	x_flush();
   3632 
   3633 	if (c == -1 || (ssize_t)LINE <= vs->linelen)
   3634 		return (-1);
   3635 
   3636 	if (vs->cbuf != buf)
   3637 		memcpy(buf, vs->cbuf, vs->linelen);
   3638 
   3639 	buf[vs->linelen++] = '\n';
   3640 
   3641 	return (vs->linelen);
   3642 }
   3643 
   3644 static int
   3645 vi_hook(int ch)
   3646 {
   3647 	static char curcmd[MAXVICMD], locpat[SRCHLEN];
   3648 	static int cmdlen, argc1, argc2;
   3649 
   3650 	switch (state) {
   3651 
   3652 	case VNORMAL:
   3653 		/* PC scancodes */
   3654 		if (!ch) switch (cmdlen = 0, (ch = x_getc())) {
   3655 		case 71: ch = '0'; goto pseudo_vi_command;
   3656 		case 72: ch = 'k'; goto pseudo_vi_command;
   3657 		case 73: ch = 'A'; goto vi_xfunc_search_up;
   3658 		case 75: ch = 'h'; goto pseudo_vi_command;
   3659 		case 77: ch = 'l'; goto pseudo_vi_command;
   3660 		case 79: ch = '$'; goto pseudo_vi_command;
   3661 		case 80: ch = 'j'; goto pseudo_vi_command;
   3662 		case 83: ch = 'x'; goto pseudo_vi_command;
   3663 		default: ch = 0; goto vi_insert_failed;
   3664 		}
   3665 		if (insert != 0) {
   3666 			if (ch == CTRL_V) {
   3667 				state = VLIT;
   3668 				ch = '^';
   3669 			}
   3670 			switch (vi_insert(ch)) {
   3671 			case -1:
   3672  vi_insert_failed:
   3673 				vi_error();
   3674 				state = VNORMAL;
   3675 				break;
   3676 			case 0:
   3677 				if (state == VLIT) {
   3678 					vs->cursor--;
   3679 					refresh(0);
   3680 				} else
   3681 					refresh(insert != 0);
   3682 				break;
   3683 			case 1:
   3684 				return (1);
   3685 			}
   3686 		} else {
   3687 			if (ctype(ch, C_CR | C_LF))
   3688 				return (1);
   3689 			cmdlen = 0;
   3690 			argc1 = 0;
   3691 			if (ctype(ch, C_DIGIT) && ord(ch) != ORD('0')) {
   3692 				argc1 = ksh_numdig(ch);
   3693 				state = VARG1;
   3694 			} else {
   3695  pseudo_vi_command:
   3696 				curcmd[cmdlen++] = ch;
   3697 				state = nextstate(ch);
   3698 				if (state == VSEARCH) {
   3699 					save_cbuf();
   3700 					vs->cursor = 0;
   3701 					vs->linelen = 0;
   3702 					if (putbuf(ch == '/' ? "/" : "?", 1,
   3703 					    false) != 0)
   3704 						return (-1);
   3705 					refresh(0);
   3706 				}
   3707 				if (state == VVERSION) {
   3708 					save_cbuf();
   3709 					vs->cursor = 0;
   3710 					vs->linelen = 0;
   3711 					putbuf(KSH_VERSION,
   3712 					    strlen(KSH_VERSION), false);
   3713 					refresh(0);
   3714 				}
   3715 			}
   3716 		}
   3717 		break;
   3718 
   3719 	case VLIT:
   3720 		if (is_bad(ch)) {
   3721 			del_range(vs->cursor, vs->cursor + 1);
   3722 			vi_error();
   3723 		} else
   3724 			vs->cbuf[vs->cursor++] = ch;
   3725 		refresh(1);
   3726 		state = VNORMAL;
   3727 		break;
   3728 
   3729 	case VVERSION:
   3730 		restore_cbuf();
   3731 		state = VNORMAL;
   3732 		refresh(0);
   3733 		break;
   3734 
   3735 	case VARG1:
   3736 		if (ctype(ch, C_DIGIT))
   3737 			argc1 = argc1 * 10 + ksh_numdig(ch);
   3738 		else {
   3739 			curcmd[cmdlen++] = ch;
   3740 			state = nextstate(ch);
   3741 		}
   3742 		break;
   3743 
   3744 	case VEXTCMD:
   3745 		argc2 = 0;
   3746 		if (ctype(ch, C_DIGIT) && ord(ch) != ORD('0')) {
   3747 			argc2 = ksh_numdig(ch);
   3748 			state = VARG2;
   3749 			return (0);
   3750 		} else {
   3751 			curcmd[cmdlen++] = ch;
   3752 			if (ch == curcmd[0])
   3753 				state = VCMD;
   3754 			else if (is_move(ch))
   3755 				state = nextstate(ch);
   3756 			else
   3757 				state = VFAIL;
   3758 		}
   3759 		break;
   3760 
   3761 	case VARG2:
   3762 		if (ctype(ch, C_DIGIT))
   3763 			argc2 = argc2 * 10 + ksh_numdig(ch);
   3764 		else {
   3765 			if (argc1 == 0)
   3766 				argc1 = argc2;
   3767 			else
   3768 				argc1 *= argc2;
   3769 			curcmd[cmdlen++] = ch;
   3770 			if (ch == curcmd[0])
   3771 				state = VCMD;
   3772 			else if (is_move(ch))
   3773 				state = nextstate(ch);
   3774 			else
   3775 				state = VFAIL;
   3776 		}
   3777 		break;
   3778 
   3779 	case VXCH:
   3780 		if (ch == CTRL_BO)
   3781 			state = VNORMAL;
   3782 		else {
   3783 			curcmd[cmdlen++] = ch;
   3784 			state = VCMD;
   3785 		}
   3786 		break;
   3787 
   3788 	case VSEARCH:
   3789 		if (ctype(ch, C_CR | C_LF) /* || ch == CTRL_BO */ ) {
   3790 			restore_cbuf();
   3791 			/* Repeat last search? */
   3792 			if (srchlen == 0) {
   3793 				if (!srchpat[0]) {
   3794 					vi_error();
   3795 					state = VNORMAL;
   3796 					refresh(0);
   3797 					return (0);
   3798 				}
   3799 			} else {
   3800 				locpat[srchlen] = '\0';
   3801 				memcpy(srchpat, locpat, srchlen + 1);
   3802 			}
   3803 			state = VCMD;
   3804 		} else if (isched(ch, edchars.erase) || ch == CTRL_H) {
   3805 			if (srchlen != 0) {
   3806 				srchlen--;
   3807 				vs->linelen -= char_len(locpat[srchlen]);
   3808 				vs->cursor = vs->linelen;
   3809 				refresh(0);
   3810 				return (0);
   3811 			}
   3812 			restore_cbuf();
   3813 			state = VNORMAL;
   3814 			refresh(0);
   3815 		} else if (isched(ch, edchars.kill)) {
   3816 			srchlen = 0;
   3817 			vs->linelen = 1;
   3818 			vs->cursor = 1;
   3819 			refresh(0);
   3820 			return (0);
   3821 		} else if (isched(ch, edchars.werase)) {
   3822 			unsigned int i, n;
   3823 			struct edstate new_es, *save_es;
   3824 
   3825 			new_es.cursor = srchlen;
   3826 			new_es.cbuf = locpat;
   3827 
   3828 			save_es = vs;
   3829 			vs = &new_es;
   3830 			n = backword(1);
   3831 			vs = save_es;
   3832 
   3833 			i = (unsigned)srchlen;
   3834 			while (--i >= n)
   3835 				vs->linelen -= char_len(locpat[i]);
   3836 			srchlen = (int)n;
   3837 			vs->cursor = vs->linelen;
   3838 			refresh(0);
   3839 			return (0);
   3840 		} else {
   3841 			if (srchlen == SRCHLEN - 1)
   3842 				vi_error();
   3843 			else {
   3844 				locpat[srchlen++] = ch;
   3845 				if (ksh_isctrl(ch)) {
   3846 					if ((size_t)vs->linelen + 2 >
   3847 					    (size_t)vs->cbufsize)
   3848 						vi_error();
   3849 					vs->cbuf[vs->linelen++] = '^';
   3850 					vs->cbuf[vs->linelen++] = ksh_unctrl(ch);
   3851 				} else {
   3852 					if (vs->linelen >= vs->cbufsize)
   3853 						vi_error();
   3854 					vs->cbuf[vs->linelen++] = ch;
   3855 				}
   3856 				vs->cursor = vs->linelen;
   3857 				refresh(0);
   3858 			}
   3859 			return (0);
   3860 		}
   3861 		break;
   3862 
   3863 	case VPREFIX2:
   3864  vi_xfunc_search_up:
   3865 		state = VFAIL;
   3866 		switch (ch) {
   3867 		case 'A':
   3868 			/* the cursor may not be at the BOL */
   3869 			if (!vs->cursor)
   3870 				break;
   3871 			/* nor further in the line than we can search for */
   3872 			if ((size_t)vs->cursor >= sizeof(srchpat) - 1)
   3873 				vs->cursor = sizeof(srchpat) - 2;
   3874 			/* anchor the search pattern */
   3875 			srchpat[0] = '^';
   3876 			/* take the current line up to the cursor */
   3877 			memmove(srchpat + 1, vs->cbuf, vs->cursor);
   3878 			srchpat[vs->cursor + 1] = '\0';
   3879 			/* set a magic flag */
   3880 			argc1 = 2 + (int)vs->cursor;
   3881 			/* and emulate a backwards history search */
   3882 			lastsearch = '/';
   3883 			*curcmd = 'n';
   3884 			goto pseudo_VCMD;
   3885 		}
   3886 		break;
   3887 	}
   3888 
   3889 	switch (state) {
   3890 	case VCMD:
   3891  pseudo_VCMD:
   3892 		state = VNORMAL;
   3893 		switch (vi_cmd(argc1, curcmd)) {
   3894 		case -1:
   3895 			vi_error();
   3896 			refresh(0);
   3897 			break;
   3898 		case 0:
   3899 			if (insert != 0)
   3900 				inslen = 0;
   3901 			refresh(insert != 0);
   3902 			break;
   3903 		case 1:
   3904 			refresh(0);
   3905 			return (1);
   3906 		case 2:
   3907 			/* back from a 'v' command - don't redraw the screen */
   3908 			return (1);
   3909 		}
   3910 		break;
   3911 
   3912 	case VREDO:
   3913 		state = VNORMAL;
   3914 		if (argc1 != 0)
   3915 			lastac = argc1;
   3916 		switch (vi_cmd(lastac, lastcmd)) {
   3917 		case -1:
   3918 			vi_error();
   3919 			refresh(0);
   3920 			break;
   3921 		case 0:
   3922 			if (insert != 0) {
   3923 				if (lastcmd[0] == 's' ||
   3924 				    ksh_eq(lastcmd[0], 'C', 'c')) {
   3925 					if (redo_insert(1) != 0)
   3926 						vi_error();
   3927 				} else {
   3928 					if (redo_insert(lastac) != 0)
   3929 						vi_error();
   3930 				}
   3931 			}
   3932 			refresh(0);
   3933 			break;
   3934 		case 1:
   3935 			refresh(0);
   3936 			return (1);
   3937 		case 2:
   3938 			/* back from a 'v' command - can't happen */
   3939 			break;
   3940 		}
   3941 		break;
   3942 
   3943 	case VFAIL:
   3944 		state = VNORMAL;
   3945 		vi_error();
   3946 		break;
   3947 	}
   3948 	return (0);
   3949 }
   3950 
   3951 static int
   3952 nextstate(int ch)
   3953 {
   3954 	if (is_extend(ch))
   3955 		return (VEXTCMD);
   3956 	else if (is_srch(ch))
   3957 		return (VSEARCH);
   3958 	else if (is_long(ch))
   3959 		return (VXCH);
   3960 	else if (ch == '.')
   3961 		return (VREDO);
   3962 	else if (ch == CTRL_V)
   3963 		return (VVERSION);
   3964 	else if (is_cmd(ch))
   3965 		return (VCMD);
   3966 	else
   3967 		return (VFAIL);
   3968 }
   3969 
   3970 static int
   3971 vi_insert(int ch)
   3972 {
   3973 	int tcursor;
   3974 
   3975 	if (isched(ch, edchars.erase) || ch == CTRL_H) {
   3976 		if (insert == REPLACE) {
   3977 			if (vs->cursor == undo->cursor) {
   3978 				vi_error();
   3979 				return (0);
   3980 			}
   3981 			if (inslen > 0)
   3982 				inslen--;
   3983 			vs->cursor--;
   3984 			if (vs->cursor >= undo->linelen)
   3985 				vs->linelen--;
   3986 			else
   3987 				vs->cbuf[vs->cursor] = undo->cbuf[vs->cursor];
   3988 		} else {
   3989 			if (vs->cursor == 0)
   3990 				return (0);
   3991 			if (inslen > 0)
   3992 				inslen--;
   3993 			vs->cursor--;
   3994 			vs->linelen--;
   3995 			memmove(&vs->cbuf[vs->cursor], &vs->cbuf[vs->cursor + 1],
   3996 			    vs->linelen - vs->cursor + 1);
   3997 		}
   3998 		expanded = NONE;
   3999 		return (0);
   4000 	}
   4001 	if (isched(ch, edchars.kill)) {
   4002 		if (vs->cursor != 0) {
   4003 			inslen = 0;
   4004 			memmove(vs->cbuf, &vs->cbuf[vs->cursor],
   4005 			    vs->linelen - vs->cursor);
   4006 			vs->linelen -= vs->cursor;
   4007 			vs->cursor = 0;
   4008 		}
   4009 		expanded = NONE;
   4010 		return (0);
   4011 	}
   4012 	if (isched(ch, edchars.werase)) {
   4013 		if (vs->cursor != 0) {
   4014 			tcursor = backword(1);
   4015 			memmove(&vs->cbuf[tcursor], &vs->cbuf[vs->cursor],
   4016 			    vs->linelen - vs->cursor);
   4017 			vs->linelen -= vs->cursor - tcursor;
   4018 			if (inslen < vs->cursor - tcursor)
   4019 				inslen = 0;
   4020 			else
   4021 				inslen -= vs->cursor - tcursor;
   4022 			vs->cursor = tcursor;
   4023 		}
   4024 		expanded = NONE;
   4025 		return (0);
   4026 	}
   4027 	/*
   4028 	 * If any chars are entered before escape, trash the saved insert
   4029 	 * buffer (if user inserts & deletes char, ibuf gets trashed and
   4030 	 * we don't want to use it)
   4031 	 */
   4032 	if (first_insert && ch != CTRL_BO)
   4033 		saved_inslen = 0;
   4034 	switch (ch) {
   4035 	case '\0':
   4036 		return (-1);
   4037 
   4038 	case '\r':
   4039 	case '\n':
   4040 		return (1);
   4041 
   4042 	case CTRL_BO:
   4043 		expanded = NONE;
   4044 		if (first_insert) {
   4045 			first_insert = false;
   4046 			if (inslen == 0) {
   4047 				inslen = saved_inslen;
   4048 				return (redo_insert(0));
   4049 			}
   4050 			lastcmd[0] = 'a';
   4051 			lastac = 1;
   4052 		}
   4053 		if (lastcmd[0] == 's' || ksh_eq(lastcmd[0], 'C', 'c'))
   4054 			return (redo_insert(0));
   4055 		else
   4056 			return (redo_insert(lastac - 1));
   4057 
   4058 	/* { start nonstandard vi commands */
   4059 	case CTRL_X:
   4060 		expand_word(0);
   4061 		break;
   4062 
   4063 	case CTRL_F:
   4064 		complete_word(0, 0);
   4065 		break;
   4066 
   4067 	case CTRL_E:
   4068 		print_expansions(vs, 0);
   4069 		break;
   4070 
   4071 	case CTRL_I:
   4072 		if (Flag(FVITABCOMPLETE)) {
   4073 			complete_word(0, 0);
   4074 			break;
   4075 		}
   4076 		/* FALLTHROUGH */
   4077 	/* end nonstandard vi commands } */
   4078 
   4079 	default:
   4080 		if (vs->linelen >= vs->cbufsize - 1)
   4081 			return (-1);
   4082 		ibuf[inslen++] = ch;
   4083 		if (insert == INSERT) {
   4084 			memmove(&vs->cbuf[vs->cursor + 1], &vs->cbuf[vs->cursor],
   4085 			    vs->linelen - vs->cursor);
   4086 			vs->linelen++;
   4087 		}
   4088 		vs->cbuf[vs->cursor++] = ch;
   4089 		if (insert == REPLACE && vs->cursor > vs->linelen)
   4090 			vs->linelen++;
   4091 		expanded = NONE;
   4092 	}
   4093 	return (0);
   4094 }
   4095 
   4096 static int
   4097 vi_cmd(int argcnt, const char *cmd)
   4098 {
   4099 	int ncursor;
   4100 	int cur, c1, c2, c3 = 0;
   4101 	int any;
   4102 	struct edstate *t;
   4103 
   4104 	if (argcnt == 0 && !is_zerocount(*cmd))
   4105 		argcnt = 1;
   4106 
   4107 	if (is_move(*cmd)) {
   4108 		if ((cur = domove(argcnt, cmd, 0)) >= 0) {
   4109 			if (cur == vs->linelen && cur != 0)
   4110 				cur--;
   4111 			vs->cursor = cur;
   4112 		} else
   4113 			return (-1);
   4114 	} else {
   4115 		/* Don't save state in middle of macro.. */
   4116 		if (is_undoable(*cmd) && !macro.p) {
   4117 			undo->winleft = vs->winleft;
   4118 			memmove(undo->cbuf, vs->cbuf, vs->linelen);
   4119 			undo->linelen = vs->linelen;
   4120 			undo->cursor = vs->cursor;
   4121 			lastac = argcnt;
   4122 			memmove(lastcmd, cmd, MAXVICMD);
   4123 		}
   4124 		switch (ord(*cmd)) {
   4125 
   4126 		case CTRL_L:
   4127 		case CTRL_R:
   4128 			redraw_line(true);
   4129 			break;
   4130 
   4131 		case ORD('@'):
   4132 			{
   4133 				static char alias[] = "_\0";
   4134 				struct tbl *ap;
   4135 				size_t olen, nlen;
   4136 				char *p, *nbuf;
   4137 
   4138 				/* lookup letter in alias list... */
   4139 				alias[1] = cmd[1];
   4140 				ap = ktsearch(&aliases, alias, hash(alias));
   4141 				if (!cmd[1] || !ap || !(ap->flag & ISSET))
   4142 					return (-1);
   4143 				/* check if this is a recursive call... */
   4144 				if ((p = (char *)macro.p))
   4145 					while ((p = strnul(p)) && p[1])
   4146 						if (*++p == cmd[1])
   4147 							return (-1);
   4148 				/* insert alias into macro buffer */
   4149 				nlen = strlen(ap->val.s) + 1;
   4150 				olen = !macro.p ? 2 :
   4151 				    macro.len - (macro.p - macro.buf);
   4152 				/*
   4153 				 * at this point, it's fairly reasonable that
   4154 				 * nlen + olen + 2 doesn't overflow
   4155 				 */
   4156 				nbuf = alloc(nlen + 1 + olen, AEDIT);
   4157 				memcpy(nbuf, ap->val.s, nlen);
   4158 				nbuf[nlen++] = cmd[1];
   4159 				if (macro.p) {
   4160 					memcpy(nbuf + nlen, macro.p, olen);
   4161 					afree(macro.buf, AEDIT);
   4162 					nlen += olen;
   4163 				} else {
   4164 					nbuf[nlen++] = '\0';
   4165 					nbuf[nlen++] = '\0';
   4166 				}
   4167 				macro.p = macro.buf = (unsigned char *)nbuf;
   4168 				macro.len = nlen;
   4169 			}
   4170 			break;
   4171 
   4172 		case ORD('a'):
   4173 			modified = 1;
   4174 			hnum = hlast;
   4175 			if (vs->linelen != 0)
   4176 				vs->cursor++;
   4177 			insert = INSERT;
   4178 			break;
   4179 
   4180 		case ORD('A'):
   4181 			modified = 1;
   4182 			hnum = hlast;
   4183 			del_range(0, 0);
   4184 			vs->cursor = vs->linelen;
   4185 			insert = INSERT;
   4186 			break;
   4187 
   4188 		case ORD('S'):
   4189 			vs->cursor = domovebeg();
   4190 			del_range(vs->cursor, vs->linelen);
   4191 			modified = 1;
   4192 			hnum = hlast;
   4193 			insert = INSERT;
   4194 			break;
   4195 
   4196 		case ORD('Y'):
   4197 			cmd = "y$";
   4198 			/* ahhhhhh... */
   4199 
   4200 			/* FALLTHROUGH */
   4201 		case ORD('c'):
   4202 		case ORD('d'):
   4203 		case ORD('y'):
   4204 			if (*cmd == cmd[1]) {
   4205 				c1 = *cmd == 'c' ? domovebeg() : 0;
   4206 				c2 = vs->linelen;
   4207 			} else if (!is_move(cmd[1]))
   4208 				return (-1);
   4209 			else {
   4210 				if ((ncursor = domove(argcnt, &cmd[1], 1)) < 0)
   4211 					return (-1);
   4212 				if (*cmd == 'c' && ksh_eq(cmd[1], 'W', 'w') &&
   4213 				    !ctype(vs->cbuf[vs->cursor], C_SPACE)) {
   4214 					do {
   4215 						--ncursor;
   4216 					} while (ctype(vs->cbuf[ncursor], C_SPACE));
   4217 					ncursor++;
   4218 				}
   4219 				if (ncursor > vs->cursor) {
   4220 					c1 = vs->cursor;
   4221 					c2 = ncursor;
   4222 				} else {
   4223 					c1 = ncursor;
   4224 					c2 = vs->cursor;
   4225 					if (cmd[1] == '%')
   4226 						c2++;
   4227 				}
   4228 			}
   4229 			if (*cmd != 'c' && c1 != c2)
   4230 				yank_range(c1, c2);
   4231 			if (*cmd != 'y') {
   4232 				del_range(c1, c2);
   4233 				vs->cursor = c1;
   4234 			}
   4235 			if (*cmd == 'c') {
   4236 				modified = 1;
   4237 				hnum = hlast;
   4238 				insert = INSERT;
   4239 			}
   4240 			break;
   4241 
   4242 		case ORD('p'):
   4243 			modified = 1;
   4244 			hnum = hlast;
   4245 			if (vs->linelen != 0)
   4246 				vs->cursor++;
   4247 			while (putbuf(ybuf, yanklen, false) == 0 &&
   4248 			    --argcnt > 0)
   4249 				;
   4250 			if (vs->cursor != 0)
   4251 				vs->cursor--;
   4252 			if (argcnt != 0)
   4253 				return (-1);
   4254 			break;
   4255 
   4256 		case ORD('P'):
   4257 			modified = 1;
   4258 			hnum = hlast;
   4259 			any = 0;
   4260 			while (putbuf(ybuf, yanklen, false) == 0 &&
   4261 			    --argcnt > 0)
   4262 				any = 1;
   4263 			if (any && vs->cursor != 0)
   4264 				vs->cursor--;
   4265 			if (argcnt != 0)
   4266 				return (-1);
   4267 			break;
   4268 
   4269 		case ORD('C'):
   4270 			modified = 1;
   4271 			hnum = hlast;
   4272 			del_range(vs->cursor, vs->linelen);
   4273 			insert = INSERT;
   4274 			break;
   4275 
   4276 		case ORD('D'):
   4277 			yank_range(vs->cursor, vs->linelen);
   4278 			del_range(vs->cursor, vs->linelen);
   4279 			if (vs->cursor != 0)
   4280 				vs->cursor--;
   4281 			break;
   4282 
   4283 		case ORD('g'):
   4284 			if (!argcnt)
   4285 				argcnt = hlast;
   4286 			/* FALLTHROUGH */
   4287 		case ORD('G'):
   4288 			if (!argcnt)
   4289 				argcnt = 1;
   4290 			else
   4291 				argcnt = hlast - (source->line - argcnt);
   4292 			if (grabhist(modified, argcnt - 1) < 0)
   4293 				return (-1);
   4294 			else {
   4295 				modified = 0;
   4296 				hnum = argcnt - 1;
   4297 			}
   4298 			break;
   4299 
   4300 		case ORD('i'):
   4301 			modified = 1;
   4302 			hnum = hlast;
   4303 			insert = INSERT;
   4304 			break;
   4305 
   4306 		case ORD('I'):
   4307 			modified = 1;
   4308 			hnum = hlast;
   4309 			vs->cursor = domovebeg();
   4310 			insert = INSERT;
   4311 			break;
   4312 
   4313 		case ORD('j'):
   4314 		case ORD('+'):
   4315 		case CTRL_N:
   4316 			if (grabhist(modified, hnum + argcnt) < 0)
   4317 				return (-1);
   4318 			else {
   4319 				modified = 0;
   4320 				hnum += argcnt;
   4321 			}
   4322 			break;
   4323 
   4324 		case ORD('k'):
   4325 		case ORD('-'):
   4326 		case CTRL_P:
   4327 			if (grabhist(modified, hnum - argcnt) < 0)
   4328 				return (-1);
   4329 			else {
   4330 				modified = 0;
   4331 				hnum -= argcnt;
   4332 			}
   4333 			break;
   4334 
   4335 		case ORD('r'):
   4336 			if (vs->linelen == 0)
   4337 				return (-1);
   4338 			modified = 1;
   4339 			hnum = hlast;
   4340 			if (cmd[1] == 0)
   4341 				vi_error();
   4342 			else {
   4343 				int n;
   4344 
   4345 				if (vs->cursor + argcnt > vs->linelen)
   4346 					return (-1);
   4347 				for (n = 0; n < argcnt; ++n)
   4348 					vs->cbuf[vs->cursor + n] = cmd[1];
   4349 				vs->cursor += n - 1;
   4350 			}
   4351 			break;
   4352 
   4353 		case ORD('R'):
   4354 			modified = 1;
   4355 			hnum = hlast;
   4356 			insert = REPLACE;
   4357 			break;
   4358 
   4359 		case ORD('s'):
   4360 			if (vs->linelen == 0)
   4361 				return (-1);
   4362 			modified = 1;
   4363 			hnum = hlast;
   4364 			if (vs->cursor + argcnt > vs->linelen)
   4365 				argcnt = vs->linelen - vs->cursor;
   4366 			del_range(vs->cursor, vs->cursor + argcnt);
   4367 			insert = INSERT;
   4368 			break;
   4369 
   4370 		case ORD('v'):
   4371 			if (!argcnt) {
   4372 				if (vs->linelen == 0)
   4373 					return (-1);
   4374 				if (modified) {
   4375 					vs->cbuf[vs->linelen] = '\0';
   4376 					histsave(&source->line, vs->cbuf,
   4377 					    HIST_STORE, true);
   4378 				} else
   4379 					argcnt = source->line + 1 -
   4380 					    (hlast - hnum);
   4381 			}
   4382 			if (argcnt)
   4383 				shf_snprintf(vs->cbuf, vs->cbufsize, Tf_sd,
   4384 				    "fc -e ${VISUAL:-${EDITOR:-vi}} --",
   4385 				    argcnt);
   4386 			else
   4387 				strlcpy(vs->cbuf,
   4388 				    "fc -e ${VISUAL:-${EDITOR:-vi}} --",
   4389 				    vs->cbufsize);
   4390 			vs->linelen = strlen(vs->cbuf);
   4391 			return (2);
   4392 
   4393 		case ORD('x'):
   4394 			if (vs->linelen == 0)
   4395 				return (-1);
   4396 			modified = 1;
   4397 			hnum = hlast;
   4398 			if (vs->cursor + argcnt > vs->linelen)
   4399 				argcnt = vs->linelen - vs->cursor;
   4400 			yank_range(vs->cursor, vs->cursor + argcnt);
   4401 			del_range(vs->cursor, vs->cursor + argcnt);
   4402 			break;
   4403 
   4404 		case ORD('X'):
   4405 			if (vs->cursor > 0) {
   4406 				modified = 1;
   4407 				hnum = hlast;
   4408 				if (vs->cursor < argcnt)
   4409 					argcnt = vs->cursor;
   4410 				yank_range(vs->cursor - argcnt, vs->cursor);
   4411 				del_range(vs->cursor - argcnt, vs->cursor);
   4412 				vs->cursor -= argcnt;
   4413 			} else
   4414 				return (-1);
   4415 			break;
   4416 
   4417 		case ORD('u'):
   4418 			t = vs;
   4419 			vs = undo;
   4420 			undo = t;
   4421 			break;
   4422 
   4423 		case ORD('U'):
   4424 			if (!modified)
   4425 				return (-1);
   4426 			if (grabhist(modified, ohnum) < 0)
   4427 				return (-1);
   4428 			modified = 0;
   4429 			hnum = ohnum;
   4430 			break;
   4431 
   4432 		case ORD('?'):
   4433 			if (hnum == hlast)
   4434 				hnum = -1;
   4435 			/* ahhh */
   4436 
   4437 			/* FALLTHROUGH */
   4438 		case ORD('/'):
   4439 			c3 = 1;
   4440 			srchlen = 0;
   4441 			lastsearch = *cmd;
   4442 			/* FALLTHROUGH */
   4443 		case ORD('n'):
   4444 		case ORD('N'):
   4445 			if (lastsearch == ' ')
   4446 				return (-1);
   4447 			if (lastsearch == '?')
   4448 				c1 = 1;
   4449 			else
   4450 				c1 = 0;
   4451 			if (*cmd == 'N')
   4452 				c1 = !c1;
   4453 			if ((c2 = grabsearch(modified, hnum,
   4454 			    c1, srchpat)) < 0) {
   4455 				if (c3) {
   4456 					restore_cbuf();
   4457 					refresh(0);
   4458 				}
   4459 				return (-1);
   4460 			} else {
   4461 				modified = 0;
   4462 				hnum = c2;
   4463 				ohnum = hnum;
   4464 			}
   4465 			if (argcnt >= 2) {
   4466 				/* flag from cursor-up command */
   4467 				vs->cursor = argcnt - 2;
   4468 				return (0);
   4469 			}
   4470 			break;
   4471 		case ORD('_'):
   4472 			{
   4473 				bool inspace;
   4474 				char *p, *sp;
   4475 
   4476 				if (histnum(-1) < 0)
   4477 					return (-1);
   4478 				p = *histpos();
   4479 				if (argcnt) {
   4480 					while (ctype(*p, C_SPACE))
   4481 						p++;
   4482 					while (*p && --argcnt) {
   4483 						while (*p && !ctype(*p, C_SPACE))
   4484 							p++;
   4485 						while (ctype(*p, C_SPACE))
   4486 							p++;
   4487 					}
   4488 					if (!*p)
   4489 						return (-1);
   4490 					sp = p;
   4491 				} else {
   4492 					sp = p;
   4493 					inspace = false;
   4494 					while (*p) {
   4495 						if (ctype(*p, C_SPACE))
   4496 							inspace = true;
   4497 						else if (inspace) {
   4498 							inspace = false;
   4499 							sp = p;
   4500 						}
   4501 						p++;
   4502 					}
   4503 					p = sp;
   4504 				}
   4505 				modified = 1;
   4506 				hnum = hlast;
   4507 				if (vs->cursor != vs->linelen)
   4508 					vs->cursor++;
   4509 				while (*p && !ctype(*p, C_SPACE)) {
   4510 					argcnt++;
   4511 					p++;
   4512 				}
   4513 				if (putbuf(T1space, 1, false) != 0 ||
   4514 				    putbuf(sp, argcnt, false) != 0) {
   4515 					if (vs->cursor != 0)
   4516 						vs->cursor--;
   4517 					return (-1);
   4518 				}
   4519 				insert = INSERT;
   4520 			}
   4521 			break;
   4522 
   4523 		case ORD('~'):
   4524 			{
   4525 				char *p;
   4526 				int i;
   4527 
   4528 				if (vs->linelen == 0)
   4529 					return (-1);
   4530 				for (i = 0; i < argcnt; i++) {
   4531 					p = &vs->cbuf[vs->cursor];
   4532 					if (ctype(*p, C_LOWER)) {
   4533 						modified = 1;
   4534 						hnum = hlast;
   4535 						*p = ksh_toupper(*p);
   4536 					} else if (ctype(*p, C_UPPER)) {
   4537 						modified = 1;
   4538 						hnum = hlast;
   4539 						*p = ksh_tolower(*p);
   4540 					}
   4541 					if (vs->cursor < vs->linelen - 1)
   4542 						vs->cursor++;
   4543 				}
   4544 				break;
   4545 			}
   4546 
   4547 		case ORD('#'):
   4548 			{
   4549 				int ret = x_do_comment(vs->cbuf, vs->cbufsize,
   4550 				    &vs->linelen);
   4551 				if (ret >= 0)
   4552 					vs->cursor = 0;
   4553 				return (ret);
   4554 			}
   4555 
   4556 		/* AT&T ksh */
   4557 		case ORD('='):
   4558 		/* Nonstandard vi/ksh */
   4559 		case CTRL_E:
   4560 			print_expansions(vs, 1);
   4561 			break;
   4562 
   4563 
   4564 		/* Nonstandard vi/ksh */
   4565 		case CTRL_I:
   4566 			if (!Flag(FVITABCOMPLETE))
   4567 				return (-1);
   4568 			complete_word(1, argcnt);
   4569 			break;
   4570 
   4571 		/* some annoying AT&T kshs */
   4572 		case CTRL_BO:
   4573 			if (!Flag(FVIESCCOMPLETE))
   4574 				return (-1);
   4575 			/* FALLTHROUGH */
   4576 		/* AT&T ksh */
   4577 		case ORD('\\'):
   4578 		/* Nonstandard vi/ksh */
   4579 		case CTRL_F:
   4580 			complete_word(1, argcnt);
   4581 			break;
   4582 
   4583 
   4584 		/* AT&T ksh */
   4585 		case ORD('*'):
   4586 		/* Nonstandard vi/ksh */
   4587 		case CTRL_X:
   4588 			expand_word(1);
   4589 			break;
   4590 
   4591 
   4592 		/* mksh: cursor movement */
   4593 		case ORD('['):
   4594 		case ORD('O'):
   4595 			state = VPREFIX2;
   4596 			if (vs->linelen != 0)
   4597 				vs->cursor++;
   4598 			insert = INSERT;
   4599 			return (0);
   4600 		}
   4601 		if (insert == 0 && vs->cursor != 0 && vs->cursor >= vs->linelen)
   4602 			vs->cursor--;
   4603 	}
   4604 	return (0);
   4605 }
   4606 
   4607 static int
   4608 domove(int argcnt, const char *cmd, int sub)
   4609 {
   4610 	int ncursor = 0, i = 0, t;
   4611 	unsigned int bcount;
   4612 
   4613 	switch (ord(*cmd)) {
   4614 	case ORD('b'):
   4615 		if (!sub && vs->cursor == 0)
   4616 			return (-1);
   4617 		ncursor = backword(argcnt);
   4618 		break;
   4619 
   4620 	case ORD('B'):
   4621 		if (!sub && vs->cursor == 0)
   4622 			return (-1);
   4623 		ncursor = Backword(argcnt);
   4624 		break;
   4625 
   4626 	case ORD('e'):
   4627 		if (!sub && vs->cursor + 1 >= vs->linelen)
   4628 			return (-1);
   4629 		ncursor = endword(argcnt);
   4630 		if (sub && ncursor < vs->linelen)
   4631 			ncursor++;
   4632 		break;
   4633 
   4634 	case ORD('E'):
   4635 		if (!sub && vs->cursor + 1 >= vs->linelen)
   4636 			return (-1);
   4637 		ncursor = Endword(argcnt);
   4638 		if (sub && ncursor < vs->linelen)
   4639 			ncursor++;
   4640 		break;
   4641 
   4642 	case ORD('f'):
   4643 	case ORD('F'):
   4644 	case ORD('t'):
   4645 	case ORD('T'):
   4646 		fsavecmd = *cmd;
   4647 		fsavech = cmd[1];
   4648 		/* FALLTHROUGH */
   4649 	case ORD(','):
   4650 	case ORD(';'):
   4651 		if (fsavecmd == ' ')
   4652 			return (-1);
   4653 		i = ksh_eq(fsavecmd, 'F', 'f');
   4654 		t = fsavecmd > 'a';
   4655 		if (*cmd == ',')
   4656 			t = !t;
   4657 		if ((ncursor = findch(fsavech, argcnt, tobool(t),
   4658 		    tobool(i))) < 0)
   4659 			return (-1);
   4660 		if (sub && t)
   4661 			ncursor++;
   4662 		break;
   4663 
   4664 	case ORD('h'):
   4665 	case CTRL_H:
   4666 		if (!sub && vs->cursor == 0)
   4667 			return (-1);
   4668 		ncursor = vs->cursor - argcnt;
   4669 		if (ncursor < 0)
   4670 			ncursor = 0;
   4671 		break;
   4672 
   4673 	case ORD(' '):
   4674 	case ORD('l'):
   4675 		if (!sub && vs->cursor + 1 >= vs->linelen)
   4676 			return (-1);
   4677 		if (vs->linelen != 0) {
   4678 			ncursor = vs->cursor + argcnt;
   4679 			if (ncursor > vs->linelen)
   4680 				ncursor = vs->linelen;
   4681 		}
   4682 		break;
   4683 
   4684 	case ORD('w'):
   4685 		if (!sub && vs->cursor + 1 >= vs->linelen)
   4686 			return (-1);
   4687 		ncursor = forwword(argcnt);
   4688 		break;
   4689 
   4690 	case ORD('W'):
   4691 		if (!sub && vs->cursor + 1 >= vs->linelen)
   4692 			return (-1);
   4693 		ncursor = Forwword(argcnt);
   4694 		break;
   4695 
   4696 	case ORD('0'):
   4697 		ncursor = 0;
   4698 		break;
   4699 
   4700 	case ORD('^'):
   4701 		ncursor = domovebeg();
   4702 		break;
   4703 
   4704 	case ORD('|'):
   4705 		ncursor = argcnt;
   4706 		if (ncursor > vs->linelen)
   4707 			ncursor = vs->linelen;
   4708 		if (ncursor)
   4709 			ncursor--;
   4710 		break;
   4711 
   4712 	case ORD('$'):
   4713 		if (vs->linelen != 0)
   4714 			ncursor = vs->linelen;
   4715 		else
   4716 			ncursor = 0;
   4717 		break;
   4718 
   4719 	case ORD('%'):
   4720 		ncursor = vs->cursor;
   4721 		while (ncursor < vs->linelen &&
   4722 		    (i = bracktype(vs->cbuf[ncursor])) == 0)
   4723 			ncursor++;
   4724 		if (ncursor == vs->linelen)
   4725 			return (-1);
   4726 		bcount = 1;
   4727 		do {
   4728 			if (i > 0) {
   4729 				if (++ncursor >= vs->linelen)
   4730 					return (-1);
   4731 			} else {
   4732 				if (--ncursor < 0)
   4733 					return (-1);
   4734 			}
   4735 			t = bracktype(vs->cbuf[ncursor]);
   4736 			if (t == i)
   4737 				bcount++;
   4738 			else if (t == -i)
   4739 				bcount--;
   4740 		} while (bcount != 0);
   4741 		if (sub && i > 0)
   4742 			ncursor++;
   4743 		break;
   4744 
   4745 	default:
   4746 		return (-1);
   4747 	}
   4748 	return (ncursor);
   4749 }
   4750 
   4751 static int
   4752 domovebeg(void)
   4753 {
   4754 	int ncursor = 0;
   4755 
   4756 	while (ncursor < vs->linelen - 1 &&
   4757 	    ctype(vs->cbuf[ncursor], C_SPACE))
   4758 		ncursor++;
   4759 	return (ncursor);
   4760 }
   4761 
   4762 static int
   4763 redo_insert(int count)
   4764 {
   4765 	while (count-- > 0)
   4766 		if (putbuf(ibuf, inslen, tobool(insert == REPLACE)) != 0)
   4767 			return (-1);
   4768 	if (vs->cursor > 0)
   4769 		vs->cursor--;
   4770 	insert = 0;
   4771 	return (0);
   4772 }
   4773 
   4774 static void
   4775 yank_range(int a, int b)
   4776 {
   4777 	yanklen = b - a;
   4778 	if (yanklen != 0)
   4779 		memmove(ybuf, &vs->cbuf[a], yanklen);
   4780 }
   4781 
   4782 static int
   4783 bracktype(int ch)
   4784 {
   4785 	switch (ord(ch)) {
   4786 
   4787 	case ORD('('):
   4788 		return (1);
   4789 
   4790 	case ORD('['):
   4791 		return (2);
   4792 
   4793 	case ORD('{'):
   4794 		return (3);
   4795 
   4796 	case ORD(')'):
   4797 		return (-1);
   4798 
   4799 	case ORD(']'):
   4800 		return (-2);
   4801 
   4802 	case ORD('}'):
   4803 		return (-3);
   4804 
   4805 	default:
   4806 		return (0);
   4807 	}
   4808 }
   4809 
   4810 /*
   4811  *	Non user interface editor routines below here
   4812  */
   4813 
   4814 static void
   4815 save_cbuf(void)
   4816 {
   4817 	memmove(holdbufp, vs->cbuf, vs->linelen);
   4818 	holdlen = vs->linelen;
   4819 	holdbufp[holdlen] = '\0';
   4820 }
   4821 
   4822 static void
   4823 restore_cbuf(void)
   4824 {
   4825 	vs->cursor = 0;
   4826 	vs->linelen = holdlen;
   4827 	memmove(vs->cbuf, holdbufp, holdlen);
   4828 }
   4829 
   4830 /* return a new edstate */
   4831 static struct edstate *
   4832 save_edstate(struct edstate *old)
   4833 {
   4834 	struct edstate *news;
   4835 
   4836 	news = alloc(sizeof(struct edstate), AEDIT);
   4837 	news->cbuf = alloc(old->cbufsize, AEDIT);
   4838 	memcpy(news->cbuf, old->cbuf, old->linelen);
   4839 	news->cbufsize = old->cbufsize;
   4840 	news->linelen = old->linelen;
   4841 	news->cursor = old->cursor;
   4842 	news->winleft = old->winleft;
   4843 	return (news);
   4844 }
   4845 
   4846 static void
   4847 restore_edstate(struct edstate *news, struct edstate *old)
   4848 {
   4849 	memcpy(news->cbuf, old->cbuf, old->linelen);
   4850 	news->linelen = old->linelen;
   4851 	news->cursor = old->cursor;
   4852 	news->winleft = old->winleft;
   4853 	free_edstate(old);
   4854 }
   4855 
   4856 static void
   4857 free_edstate(struct edstate *old)
   4858 {
   4859 	afree(old->cbuf, AEDIT);
   4860 	afree(old, AEDIT);
   4861 }
   4862 
   4863 /*
   4864  * this is used for calling x_escape() in complete_word()
   4865  */
   4866 static int
   4867 x_vi_putbuf(const char *s, size_t len)
   4868 {
   4869 	return (putbuf(s, len, false));
   4870 }
   4871 
   4872 static int
   4873 putbuf(const char *buf, ssize_t len, bool repl)
   4874 {
   4875 	if (len == 0)
   4876 		return (0);
   4877 	if (repl) {
   4878 		if (vs->cursor + len >= vs->cbufsize)
   4879 			return (-1);
   4880 		if (vs->cursor + len > vs->linelen)
   4881 			vs->linelen = vs->cursor + len;
   4882 	} else {
   4883 		if (vs->linelen + len >= vs->cbufsize)
   4884 			return (-1);
   4885 		memmove(&vs->cbuf[vs->cursor + len], &vs->cbuf[vs->cursor],
   4886 		    vs->linelen - vs->cursor);
   4887 		vs->linelen += len;
   4888 	}
   4889 	memmove(&vs->cbuf[vs->cursor], buf, len);
   4890 	vs->cursor += len;
   4891 	return (0);
   4892 }
   4893 
   4894 static void
   4895 del_range(int a, int b)
   4896 {
   4897 	if (vs->linelen != b)
   4898 		memmove(&vs->cbuf[a], &vs->cbuf[b], vs->linelen - b);
   4899 	vs->linelen -= b - a;
   4900 }
   4901 
   4902 static int
   4903 findch(int ch, int cnt, bool forw, bool incl)
   4904 {
   4905 	int ncursor;
   4906 
   4907 	if (vs->linelen == 0)
   4908 		return (-1);
   4909 	ncursor = vs->cursor;
   4910 	while (cnt--) {
   4911 		do {
   4912 			if (forw) {
   4913 				if (++ncursor == vs->linelen)
   4914 					return (-1);
   4915 			} else {
   4916 				if (--ncursor < 0)
   4917 					return (-1);
   4918 			}
   4919 		} while (vs->cbuf[ncursor] != ch);
   4920 	}
   4921 	if (!incl) {
   4922 		if (forw)
   4923 			ncursor--;
   4924 		else
   4925 			ncursor++;
   4926 	}
   4927 	return (ncursor);
   4928 }
   4929 
   4930 static int
   4931 forwword(int argcnt)
   4932 {
   4933 	int ncursor;
   4934 
   4935 	ncursor = vs->cursor;
   4936 	while (ncursor < vs->linelen && argcnt--) {
   4937 		if (ctype(vs->cbuf[ncursor], C_ALNUX))
   4938 			while (ncursor < vs->linelen &&
   4939 			    ctype(vs->cbuf[ncursor], C_ALNUX))
   4940 				ncursor++;
   4941 		else if (!ctype(vs->cbuf[ncursor], C_SPACE))
   4942 			while (ncursor < vs->linelen &&
   4943 			    !ctype(vs->cbuf[ncursor], C_ALNUX | C_SPACE))
   4944 				ncursor++;
   4945 		while (ncursor < vs->linelen &&
   4946 		    ctype(vs->cbuf[ncursor], C_SPACE))
   4947 			ncursor++;
   4948 	}
   4949 	return (ncursor);
   4950 }
   4951 
   4952 static int
   4953 backword(int argcnt)
   4954 {
   4955 	int ncursor;
   4956 
   4957 	ncursor = vs->cursor;
   4958 	while (ncursor > 0 && argcnt--) {
   4959 		while (--ncursor > 0 && ctype(vs->cbuf[ncursor], C_SPACE))
   4960 			;
   4961 		if (ncursor > 0) {
   4962 			if (ctype(vs->cbuf[ncursor], C_ALNUX))
   4963 				while (--ncursor >= 0 &&
   4964 				    ctype(vs->cbuf[ncursor], C_ALNUX))
   4965 					;
   4966 			else
   4967 				while (--ncursor >= 0 &&
   4968 				    !ctype(vs->cbuf[ncursor], C_ALNUX | C_SPACE))
   4969 					;
   4970 			ncursor++;
   4971 		}
   4972 	}
   4973 	return (ncursor);
   4974 }
   4975 
   4976 static int
   4977 endword(int argcnt)
   4978 {
   4979 	int ncursor;
   4980 
   4981 	ncursor = vs->cursor;
   4982 	while (ncursor < vs->linelen && argcnt--) {
   4983 		while (++ncursor < vs->linelen - 1 &&
   4984 		    ctype(vs->cbuf[ncursor], C_SPACE))
   4985 			;
   4986 		if (ncursor < vs->linelen - 1) {
   4987 			if (ctype(vs->cbuf[ncursor], C_ALNUX))
   4988 				while (++ncursor < vs->linelen &&
   4989 				    ctype(vs->cbuf[ncursor], C_ALNUX))
   4990 					;
   4991 			else
   4992 				while (++ncursor < vs->linelen &&
   4993 				    !ctype(vs->cbuf[ncursor], C_ALNUX | C_SPACE))
   4994 					;
   4995 			ncursor--;
   4996 		}
   4997 	}
   4998 	return (ncursor);
   4999 }
   5000 
   5001 static int
   5002 Forwword(int argcnt)
   5003 {
   5004 	int ncursor;
   5005 
   5006 	ncursor = vs->cursor;
   5007 	while (ncursor < vs->linelen && argcnt--) {
   5008 		while (ncursor < vs->linelen &&
   5009 		    !ctype(vs->cbuf[ncursor], C_SPACE))
   5010 			ncursor++;
   5011 		while (ncursor < vs->linelen &&
   5012 		    ctype(vs->cbuf[ncursor], C_SPACE))
   5013 			ncursor++;
   5014 	}
   5015 	return (ncursor);
   5016 }
   5017 
   5018 static int
   5019 Backword(int argcnt)
   5020 {
   5021 	int ncursor;
   5022 
   5023 	ncursor = vs->cursor;
   5024 	while (ncursor > 0 && argcnt--) {
   5025 		while (--ncursor >= 0 && ctype(vs->cbuf[ncursor], C_SPACE))
   5026 			;
   5027 		while (ncursor >= 0 && !ctype(vs->cbuf[ncursor], C_SPACE))
   5028 			ncursor--;
   5029 		ncursor++;
   5030 	}
   5031 	return (ncursor);
   5032 }
   5033 
   5034 static int
   5035 Endword(int argcnt)
   5036 {
   5037 	int ncursor;
   5038 
   5039 	ncursor = vs->cursor;
   5040 	while (ncursor < vs->linelen - 1 && argcnt--) {
   5041 		while (++ncursor < vs->linelen - 1 &&
   5042 		    ctype(vs->cbuf[ncursor], C_SPACE))
   5043 			;
   5044 		if (ncursor < vs->linelen - 1) {
   5045 			while (++ncursor < vs->linelen &&
   5046 			    !ctype(vs->cbuf[ncursor], C_SPACE))
   5047 				;
   5048 			ncursor--;
   5049 		}
   5050 	}
   5051 	return (ncursor);
   5052 }
   5053 
   5054 static int
   5055 grabhist(int save, int n)
   5056 {
   5057 	char *hptr;
   5058 
   5059 	if (n < 0 || n > hlast)
   5060 		return (-1);
   5061 	if (n == hlast) {
   5062 		restore_cbuf();
   5063 		ohnum = n;
   5064 		return (0);
   5065 	}
   5066 	(void)histnum(n);
   5067 	if ((hptr = *histpos()) == NULL) {
   5068 		internal_warningf("grabhist: bad history array");
   5069 		return (-1);
   5070 	}
   5071 	if (save)
   5072 		save_cbuf();
   5073 	if ((vs->linelen = strlen(hptr)) >= vs->cbufsize)
   5074 		vs->linelen = vs->cbufsize - 1;
   5075 	memmove(vs->cbuf, hptr, vs->linelen);
   5076 	vs->cursor = 0;
   5077 	ohnum = n;
   5078 	return (0);
   5079 }
   5080 
   5081 static int
   5082 grabsearch(int save, int start, int fwd, const char *pat)
   5083 {
   5084 	char *hptr;
   5085 	int hist;
   5086 	bool anchored;
   5087 
   5088 	if ((start == 0 && fwd == 0) || (start >= hlast - 1 && fwd == 1))
   5089 		return (-1);
   5090 	if (fwd)
   5091 		start++;
   5092 	else
   5093 		start--;
   5094 	anchored = *pat == '^' ? (++pat, true) : false;
   5095 	if ((hist = findhist(start, fwd, pat, anchored)) < 0) {
   5096 		/* (start != 0 && fwd && match(holdbufp, pat) >= 0) */
   5097 		if (start != 0 && fwd && strcmp(holdbufp, pat) >= 0) {
   5098 			restore_cbuf();
   5099 			return (0);
   5100 		} else
   5101 			return (-1);
   5102 	}
   5103 	if (save)
   5104 		save_cbuf();
   5105 	histnum(hist);
   5106 	hptr = *histpos();
   5107 	if ((vs->linelen = strlen(hptr)) >= vs->cbufsize)
   5108 		vs->linelen = vs->cbufsize - 1;
   5109 	memmove(vs->cbuf, hptr, vs->linelen);
   5110 	vs->cursor = 0;
   5111 	return (hist);
   5112 }
   5113 
   5114 static void
   5115 redraw_line(bool newl)
   5116 {
   5117 	if (wbuf_len)
   5118 		memset(wbuf[win], ' ', wbuf_len);
   5119 	if (newl) {
   5120 		x_putc('\r');
   5121 		x_putc('\n');
   5122 	}
   5123 	x_pprompt();
   5124 	morec = ' ';
   5125 }
   5126 
   5127 static void
   5128 refresh(int leftside)
   5129 {
   5130 	if (leftside < 0)
   5131 		leftside = lastref;
   5132 	else
   5133 		lastref = leftside;
   5134 	if (outofwin())
   5135 		rewindow();
   5136 	display(wbuf[1 - win], wbuf[win], leftside);
   5137 	win = 1 - win;
   5138 }
   5139 
   5140 static int
   5141 outofwin(void)
   5142 {
   5143 	int cur, col;
   5144 
   5145 	if (vs->cursor < vs->winleft)
   5146 		return (1);
   5147 	col = 0;
   5148 	cur = vs->winleft;
   5149 	while (cur < vs->cursor)
   5150 		col = newcol((unsigned char)vs->cbuf[cur++], col);
   5151 	if (col >= winwidth)
   5152 		return (1);
   5153 	return (0);
   5154 }
   5155 
   5156 static void
   5157 rewindow(void)
   5158 {
   5159 	int tcur, tcol;
   5160 	int holdcur1, holdcol1;
   5161 	int holdcur2, holdcol2;
   5162 
   5163 	holdcur1 = holdcur2 = tcur = 0;
   5164 	holdcol1 = holdcol2 = tcol = 0;
   5165 	while (tcur < vs->cursor) {
   5166 		if (tcol - holdcol2 > winwidth / 2) {
   5167 			holdcur1 = holdcur2;
   5168 			holdcol1 = holdcol2;
   5169 			holdcur2 = tcur;
   5170 			holdcol2 = tcol;
   5171 		}
   5172 		tcol = newcol((unsigned char)vs->cbuf[tcur++], tcol);
   5173 	}
   5174 	while (tcol - holdcol1 > winwidth / 2)
   5175 		holdcol1 = newcol((unsigned char)vs->cbuf[holdcur1++],
   5176 		    holdcol1);
   5177 	vs->winleft = holdcur1;
   5178 }
   5179 
   5180 static int
   5181 newcol(unsigned char ch, int col)
   5182 {
   5183 	if (ch == '\t')
   5184 		return ((col | 7) + 1);
   5185 	return (col + char_len(ch));
   5186 }
   5187 
   5188 static void
   5189 display(char *wb1, char *wb2, int leftside)
   5190 {
   5191 	unsigned char ch;
   5192 	char *twb1, *twb2, mc;
   5193 	int cur, col, cnt;
   5194 	int ncol = 0;
   5195 	int moreright;
   5196 
   5197 	col = 0;
   5198 	cur = vs->winleft;
   5199 	moreright = 0;
   5200 	twb1 = wb1;
   5201 	while (col < winwidth && cur < vs->linelen) {
   5202 		if (cur == vs->cursor && leftside)
   5203 			ncol = col + pwidth;
   5204 		if ((ch = vs->cbuf[cur]) == '\t')
   5205 			do {
   5206 				*twb1++ = ' ';
   5207 			} while (++col < winwidth && (col & 7) != 0);
   5208 		else if (col < winwidth) {
   5209 			if (ksh_isctrl(ch)) {
   5210 				*twb1++ = '^';
   5211 				if (++col < winwidth) {
   5212 					*twb1++ = ksh_unctrl(ch);
   5213 					col++;
   5214 				}
   5215 			} else {
   5216 				*twb1++ = ch;
   5217 				col++;
   5218 			}
   5219 		}
   5220 		if (cur == vs->cursor && !leftside)
   5221 			ncol = col + pwidth - 1;
   5222 		cur++;
   5223 	}
   5224 	if (cur == vs->cursor)
   5225 		ncol = col + pwidth;
   5226 	if (col < winwidth) {
   5227 		while (col < winwidth) {
   5228 			*twb1++ = ' ';
   5229 			col++;
   5230 		}
   5231 	} else
   5232 		moreright++;
   5233 	*twb1 = ' ';
   5234 
   5235 	col = pwidth;
   5236 	cnt = winwidth;
   5237 	twb1 = wb1;
   5238 	twb2 = wb2;
   5239 	while (cnt--) {
   5240 		if (*twb1 != *twb2) {
   5241 			if (x_col != col)
   5242 				ed_mov_opt(col, wb1);
   5243 			x_putc(*twb1);
   5244 			x_col++;
   5245 		}
   5246 		twb1++;
   5247 		twb2++;
   5248 		col++;
   5249 	}
   5250 	if (vs->winleft > 0 && moreright)
   5251 		/*
   5252 		 * POSIX says to use * for this but that is a globbing
   5253 		 * character and may confuse people; + is more innocuous
   5254 		 */
   5255 		mc = '+';
   5256 	else if (vs->winleft > 0)
   5257 		mc = '<';
   5258 	else if (moreright)
   5259 		mc = '>';
   5260 	else
   5261 		mc = ' ';
   5262 	if (mc != morec) {
   5263 		ed_mov_opt(pwidth + winwidth + 1, wb1);
   5264 		x_putc(mc);
   5265 		x_col++;
   5266 		morec = mc;
   5267 	}
   5268 	if (x_col != ncol)
   5269 		ed_mov_opt(ncol, wb1);
   5270 }
   5271 
   5272 static void
   5273 ed_mov_opt(int col, char *wb)
   5274 {
   5275 	if (col < x_col) {
   5276 		if (col + 1 < x_col - col) {
   5277 			x_putc('\r');
   5278 			x_pprompt();
   5279 			while (x_col++ < col)
   5280 				x_putcf(*wb++);
   5281 		} else {
   5282 			while (x_col-- > col)
   5283 				x_putc('\b');
   5284 		}
   5285 	} else {
   5286 		wb = &wb[x_col - pwidth];
   5287 		while (x_col++ < col)
   5288 			x_putcf(*wb++);
   5289 	}
   5290 	x_col = col;
   5291 }
   5292 
   5293 
   5294 /* replace word with all expansions (ie, expand word*) */
   5295 static int
   5296 expand_word(int cmd)
   5297 {
   5298 	static struct edstate *buf;
   5299 	int rval = 0, nwords, start, end, i;
   5300 	char **words;
   5301 
   5302 	/* Undo previous expansion */
   5303 	if (cmd == 0 && expanded == EXPAND && buf) {
   5304 		restore_edstate(vs, buf);
   5305 		buf = 0;
   5306 		expanded = NONE;
   5307 		return (0);
   5308 	}
   5309 	if (buf) {
   5310 		free_edstate(buf);
   5311 		buf = 0;
   5312 	}
   5313 
   5314 	i = XCF_COMMAND_FILE | XCF_FULLPATH;
   5315 	nwords = x_cf_glob(&i, vs->cbuf, vs->linelen, vs->cursor,
   5316 	    &start, &end, &words);
   5317 	if (nwords == 0) {
   5318 		vi_error();
   5319 		return (-1);
   5320 	}
   5321 
   5322 	buf = save_edstate(vs);
   5323 	expanded = EXPAND;
   5324 	del_range(start, end);
   5325 	vs->cursor = start;
   5326 	i = 0;
   5327 	while (i < nwords) {
   5328 		if (x_escape(words[i], strlen(words[i]), x_vi_putbuf) != 0) {
   5329 			rval = -1;
   5330 			break;
   5331 		}
   5332 		if (++i < nwords && putbuf(T1space, 1, false) != 0) {
   5333 			rval = -1;
   5334 			break;
   5335 		}
   5336 	}
   5337 	i = buf->cursor - end;
   5338 	if (rval == 0 && i > 0)
   5339 		vs->cursor += i;
   5340 	modified = 1;
   5341 	hnum = hlast;
   5342 	insert = INSERT;
   5343 	lastac = 0;
   5344 	refresh(0);
   5345 	return (rval);
   5346 }
   5347 
   5348 static int
   5349 complete_word(int cmd, int count)
   5350 {
   5351 	static struct edstate *buf;
   5352 	int rval, nwords, start, end, flags;
   5353 	size_t match_len;
   5354 	char **words;
   5355 	char *match;
   5356 	bool is_unique;
   5357 
   5358 	/* Undo previous completion */
   5359 	if (cmd == 0 && expanded == COMPLETE && buf) {
   5360 		print_expansions(buf, 0);
   5361 		expanded = PRINT;
   5362 		return (0);
   5363 	}
   5364 	if (cmd == 0 && expanded == PRINT && buf) {
   5365 		restore_edstate(vs, buf);
   5366 		buf = 0;
   5367 		expanded = NONE;
   5368 		return (0);
   5369 	}
   5370 	if (buf) {
   5371 		free_edstate(buf);
   5372 		buf = 0;
   5373 	}
   5374 
   5375 	/*
   5376 	 * XCF_FULLPATH for count 'cause the menu printed by
   5377 	 * print_expansions() was done this way.
   5378 	 */
   5379 	flags = XCF_COMMAND_FILE;
   5380 	if (count)
   5381 		flags |= XCF_FULLPATH;
   5382 	nwords = x_cf_glob(&flags, vs->cbuf, vs->linelen, vs->cursor,
   5383 	    &start, &end, &words);
   5384 	if (nwords == 0) {
   5385 		vi_error();
   5386 		return (-1);
   5387 	}
   5388 	if (count) {
   5389 		int i;
   5390 
   5391 		count--;
   5392 		if (count >= nwords) {
   5393 			vi_error();
   5394 			x_print_expansions(nwords, words,
   5395 			    tobool(flags & XCF_IS_COMMAND));
   5396 			x_free_words(nwords, words);
   5397 			redraw_line(false);
   5398 			return (-1);
   5399 		}
   5400 		/*
   5401 		 * Expand the count'th word to its basename
   5402 		 */
   5403 		if (flags & XCF_IS_COMMAND) {
   5404 			match = words[count] +
   5405 			    x_basename(words[count], NULL);
   5406 			/* If more than one possible match, use full path */
   5407 			for (i = 0; i < nwords; i++)
   5408 				if (i != count &&
   5409 				    strcmp(words[i] + x_basename(words[i],
   5410 				    NULL), match) == 0) {
   5411 					match = words[count];
   5412 					break;
   5413 				}
   5414 		} else
   5415 			match = words[count];
   5416 		match_len = strlen(match);
   5417 		is_unique = true;
   5418 		/* expanded = PRINT;	next call undo */
   5419 	} else {
   5420 		match = words[0];
   5421 		match_len = x_longest_prefix(nwords, words);
   5422 		/* next call will list completions */
   5423 		expanded = COMPLETE;
   5424 		is_unique = nwords == 1;
   5425 	}
   5426 
   5427 	buf = save_edstate(vs);
   5428 	del_range(start, end);
   5429 	vs->cursor = start;
   5430 
   5431 	/*
   5432 	 * escape all shell-sensitive characters and put the result into
   5433 	 * command buffer
   5434 	 */
   5435 	rval = x_escape(match, match_len, x_vi_putbuf);
   5436 
   5437 	if (rval == 0 && is_unique) {
   5438 		/*
   5439 		 * If exact match, don't undo. Allows directory completions
   5440 		 * to be used (ie, complete the next portion of the path).
   5441 		 */
   5442 		expanded = NONE;
   5443 
   5444 		/*
   5445 		 * append a space if this is a non-directory match
   5446 		 * and not a parameter or homedir substitution
   5447 		 */
   5448 		if (match_len > 0 && !mksh_cdirsep(match[match_len - 1]) &&
   5449 		    !(flags & XCF_IS_NOSPACE))
   5450 			rval = putbuf(T1space, 1, false);
   5451 	}
   5452 	x_free_words(nwords, words);
   5453 
   5454 	modified = 1;
   5455 	hnum = hlast;
   5456 	insert = INSERT;
   5457 	/* prevent this from being redone... */
   5458 	lastac = 0;
   5459 	refresh(0);
   5460 
   5461 	return (rval);
   5462 }
   5463 
   5464 static int
   5465 print_expansions(struct edstate *est, int cmd MKSH_A_UNUSED)
   5466 {
   5467 	int start, end, nwords, i;
   5468 	char **words;
   5469 
   5470 	i = XCF_COMMAND_FILE | XCF_FULLPATH;
   5471 	nwords = x_cf_glob(&i, est->cbuf, est->linelen, est->cursor,
   5472 	    &start, &end, &words);
   5473 	if (nwords == 0) {
   5474 		vi_error();
   5475 		return (-1);
   5476 	}
   5477 	x_print_expansions(nwords, words, tobool(i & XCF_IS_COMMAND));
   5478 	x_free_words(nwords, words);
   5479 	redraw_line(false);
   5480 	return (0);
   5481 }
   5482 #endif /* !MKSH_S_NOVI */
   5483 
   5484 /* Similar to x_zotc(emacs.c), but no tab weirdness */
   5485 static void
   5486 x_vi_zotc(int c)
   5487 {
   5488 	if (ksh_isctrl(c)) {
   5489 		x_putc('^');
   5490 		c = ksh_unctrl(c);
   5491 	}
   5492 	x_putc(c);
   5493 }
   5494 
   5495 #if !MKSH_S_NOVI
   5496 static void
   5497 vi_error(void)
   5498 {
   5499 	/* Beem out of any macros as soon as an error occurs */
   5500 	vi_macro_reset();
   5501 	x_putc(KSH_BEL);
   5502 	x_flush();
   5503 }
   5504 
   5505 static void
   5506 vi_macro_reset(void)
   5507 {
   5508 	if (macro.p) {
   5509 		afree(macro.buf, AEDIT);
   5510 		memset((char *)&macro, 0, sizeof(macro));
   5511 	}
   5512 }
   5513 #endif /* !MKSH_S_NOVI */
   5514 
   5515 /* called from main.c */
   5516 void
   5517 x_init(void)
   5518 {
   5519 	int i, j;
   5520 
   5521 	/*
   5522 	 * set edchars to force initial binding, except we need
   5523 	 * default values for ^W for some deficient systems
   5524 	 */
   5525 	edchars.erase = edchars.kill = edchars.intr = edchars.quit =
   5526 	    edchars.eof = EDCHAR_INITIAL;
   5527 	edchars.werase = 027;
   5528 
   5529 	/* command line editing specific memory allocation */
   5530 	ainit(AEDIT);
   5531 	holdbufp = alloc(LINE, AEDIT);
   5532 
   5533 	/* initialise Emacs command line editing mode */
   5534 	x_nextcmd = -1;
   5535 
   5536 	x_tab = alloc2(X_NTABS, sizeof(*x_tab), AEDIT);
   5537 	for (j = 0; j < X_TABSZ; j++)
   5538 		x_tab[0][j] = XFUNC_insert;
   5539 	for (i = 1; i < X_NTABS; i++)
   5540 		for (j = 0; j < X_TABSZ; j++)
   5541 			x_tab[i][j] = XFUNC_error;
   5542 	for (i = 0; i < (int)NELEM(x_defbindings); i++)
   5543 		x_tab[x_defbindings[i].xdb_tab][x_defbindings[i].xdb_char]
   5544 		    = x_defbindings[i].xdb_func;
   5545 
   5546 #ifndef MKSH_SMALL
   5547 	x_atab = alloc2(X_NTABS, sizeof(*x_atab), AEDIT);
   5548 	for (i = 1; i < X_NTABS; i++)
   5549 		for (j = 0; j < X_TABSZ; j++)
   5550 			x_atab[i][j] = NULL;
   5551 #endif
   5552 }
   5553 
   5554 #ifdef DEBUG_LEAKS
   5555 void
   5556 x_done(void)
   5557 {
   5558 	if (x_tab != NULL)
   5559 		afreeall(AEDIT);
   5560 }
   5561 #endif
   5562 
   5563 void
   5564 x_initterm(const char *termtype)
   5565 {
   5566 	/* default must be 0 (bss) */
   5567 	x_term_mode = 0;
   5568 	/* this is what tmux uses, don't ask me about it */
   5569 	if (!strcmp(termtype, "screen") || !strncmp(termtype, "screen-", 7))
   5570 		x_term_mode = 1;
   5571 }
   5572 
   5573 #ifndef MKSH_SMALL
   5574 static char *
   5575 x_eval_region_helper(const char *cmd, size_t len)
   5576 {
   5577 	char * volatile cp;
   5578 	newenv(E_ERRH);
   5579 
   5580 	if (!kshsetjmp(e->jbuf)) {
   5581 		char *wds = alloc(len + 3, ATEMP);
   5582 
   5583 		wds[0] = FUNASUB;
   5584 		memcpy(wds + 1, cmd, len);
   5585 		wds[len + 1] = '\0';
   5586 		wds[len + 2] = EOS;
   5587 
   5588 		cp = evalstr(wds, DOSCALAR);
   5589 		afree(wds, ATEMP);
   5590 		strdupx(cp, cp, AEDIT);
   5591 	} else
   5592 		cp = NULL;
   5593 	quitenv(NULL);
   5594 	return (cp);
   5595 }
   5596 
   5597 static int
   5598 x_eval_region(int c MKSH_A_UNUSED)
   5599 {
   5600 	char *evbeg, *evend, *cp;
   5601 	size_t newlen;
   5602 	/* only for LINE overflow checking */
   5603 	size_t restlen;
   5604 
   5605 	if (xmp == NULL) {
   5606 		evbeg = xbuf;
   5607 		evend = xep;
   5608 	} else if (xmp < xcp) {
   5609 		evbeg = xmp;
   5610 		evend = xcp;
   5611 	} else {
   5612 		evbeg = xcp;
   5613 		evend = xmp;
   5614 	}
   5615 
   5616 	x_e_putc2('\r');
   5617 	x_clrtoeol(' ', false);
   5618 	x_flush();
   5619 	x_mode(false);
   5620 	cp = x_eval_region_helper(evbeg, evend - evbeg);
   5621 	x_mode(true);
   5622 
   5623 	if (cp == NULL) {
   5624 		/* command cannot be parsed */
   5625  x_eval_region_err:
   5626 		x_e_putc2(KSH_BEL);
   5627 		x_redraw('\r');
   5628 		return (KSTD);
   5629 	}
   5630 
   5631 	newlen = strlen(cp);
   5632 	restlen = xep - evend;
   5633 	/* check for LINE overflow, until this is dynamically allocated */
   5634 	if (evbeg + newlen + restlen >= xend)
   5635 		goto x_eval_region_err;
   5636 
   5637 	xmp = evbeg;
   5638 	xcp = evbeg + newlen;
   5639 	xep = xcp + restlen;
   5640 	memmove(xcp, evend, restlen + /* NUL */ 1);
   5641 	memcpy(xmp, cp, newlen);
   5642 	afree(cp, AEDIT);
   5643 	x_adjust();
   5644 	x_modified();
   5645 	return (KSTD);
   5646 }
   5647 #endif /* !MKSH_SMALL */
   5648 #endif /* !MKSH_NO_CMDLINE_EDITING */
   5649