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