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