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