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