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