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