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