oksh-noxz

[fork] Portable OpenBSD ksh, based on the Public Domain Korn Shell (pdksh).
git clone https://noxz.tech/git/oksh-noxz.git
Log | Files | Tags

emacs.c
1/*	$OpenBSD: emacs.c,v 1.90 2023/06/21 22:22:08 millert Exp $	*/
2
3/*
4 *  Emacs-like command line editing and history
5 *
6 *  created by Ron Natalie at BRL
7 *  modified by Doug Kingston, Doug Gwyn, and Lou Salkind
8 *  adapted to PD ksh by Eric Gisin
9 *
10 * partial rewrite by Marco Peereboom <marco@openbsd.org>
11 * under the same license
12 */
13
14#include "config.h"
15#ifdef EMACS
16
17#include <sys/stat.h>
18
19#include <ctype.h>
20#include <stdio.h>
21#include <stdlib.h>
22#include <string.h>
23#if !defined(SMALL) && !defined(NO_CURSES)
24#ifdef HAVE_CURSES
25# include <term.h>
26# include <curses.h>
27#elif defined(HAVE_NCURSES)
28# include <term.h>
29# include <ncurses.h>
30#elif defined(HAVE_NCURSESNCURSES)
31# include <ncurses/term.h>
32# include <ncurses/ncurses.h>
33#endif
34#endif
35
36#include "sys-queue.h"
37#include "sh.h"
38#include "edit.h"
39
40static	Area	aedit;
41#define	AEDIT	&aedit		/* area for kill ring and macro defns */
42
43/* values returned by keyboard functions */
44#define	KSTD	0
45#define	KEOL	1		/* ^M, ^J */
46#define	KINTR	2		/* ^G, ^C */
47
48typedef int (*kb_func)(int);
49
50struct	x_ftab {
51	kb_func		xf_func;
52	const char	*xf_name;
53	short		xf_flags;
54};
55
56#define XF_ARG		1	/* command takes number prefix */
57#define	XF_NOBIND	2	/* not allowed to bind to function */
58#define	XF_PREFIX	4	/* function sets prefix */
59
60/* Separator for completion */
61#define	is_cfs(c)	(c == ' ' || c == '\t' || c == '"' || c == '\'')
62
63/* Separator for motion */
64#define	is_mfs(c)	(!(isalnum((unsigned char)c) || \
65			c == '_' || c == '$' || c & 0x80))
66
67/* Arguments for do_complete()
68 * 0 = enumerate  M-= complete as much as possible and then list
69 * 1 = complete   M-Esc
70 * 2 = list       M-?
71 */
72typedef enum {
73	CT_LIST,	/* list the possible completions */
74	CT_COMPLETE,	/* complete to longest prefix */
75	CT_COMPLIST	/* complete and then list (if non-exact) */
76} Comp_type;
77
78/* keybindings */
79struct kb_entry {
80	TAILQ_ENTRY(kb_entry)	entry;
81	unsigned char		*seq;
82	int			len;
83	struct x_ftab		*ftab;
84	void			*args;
85};
86TAILQ_HEAD(kb_list, kb_entry);
87struct kb_list			kblist = TAILQ_HEAD_INITIALIZER(kblist);
88
89/* { from 4.9 edit.h */
90/*
91 * The following are used for my horizontal scrolling stuff
92 */
93static char    *xbuf;		/* beg input buffer */
94static char    *xend;		/* end input buffer */
95static char    *xcp;		/* current position */
96static char    *xep;		/* current end */
97static char    *xbp;		/* start of visible portion of input buffer */
98static char    *xlp;		/* last byte visible on screen */
99static int	x_adj_ok;
100/*
101 * we use x_adj_done so that functions can tell
102 * whether x_adjust() has been called while they are active.
103 */
104static int	x_adj_done;
105
106static int	xx_cols;
107static int	x_col;
108static int	x_displen;
109static int	x_arg;		/* general purpose arg */
110static int	x_arg_defaulted;/* x_arg not explicitly set; defaulted to 1 */
111
112static int	xlp_valid;
113/* end from 4.9 edit.h } */
114static	int	x_tty;		/* are we on a tty? */
115static	int	x_bind_quiet;	/* be quiet when binding keys */
116static int	(*x_last_command)(int);
117
118static	char   **x_histp;	/* history position */
119static	int	x_nextcmd;	/* for newline-and-next */
120static	char	*xmp;		/* mark pointer */
121#define	KILLSIZE	20
122static	char	*killstack[KILLSIZE];
123static	int	killsp, killtp;
124static	int	x_literal_set;
125static	int	x_arg_set;
126static	char	*macro_args;
127static	int	prompt_skip;
128static	int	prompt_redraw;
129
130static int	x_ins(char *);
131static void	x_delete(int, int);
132static int	x_bword(void);
133static int	x_fword(void);
134static void	x_goto(char *);
135static void	x_bs(int);
136static int	x_size_str(char *);
137static int	x_size(int);
138static void	x_zots(char *);
139static void	x_zotc(int);
140static void	x_load_hist(char **);
141static int	x_search(char *, int, int);
142static int	x_match(char *, char *);
143static void	x_redraw(int);
144static void	x_push(int);
145static void	x_adjust(void);
146static void	x_e_ungetc(int);
147static int	x_e_getc(void);
148static int	x_e_getu8(char *, int);
149static void	x_e_putc(int);
150static void	x_e_puts(const char *);
151static int	x_comment(int);
152static int	x_fold_case(int);
153static char	*x_lastcp(void);
154static void	do_complete(int, Comp_type);
155static int	isu8cont(unsigned char);
156
157/* proto's for keybindings */
158static int	x_abort(int);
159static int	x_beg_hist(int);
160static int	x_clear_screen(int);
161static int	x_comp_comm(int);
162static int	x_comp_file(int);
163static int	x_complete(int);
164static int	x_del_back(int);
165static int	x_del_bword(int);
166static int	x_del_char(int);
167static int	x_del_fword(int);
168static int	x_del_line(int);
169static int	x_draw_line(int);
170static int	x_end_hist(int);
171static int	x_end_of_text(int);
172static int	x_enumerate(int);
173static int	x_eot_del(int);
174static int	x_error(int);
175static int	x_goto_hist(int);
176static int	x_ins_string(int);
177static int	x_insert(int);
178static int	x_kill(int);
179static int	x_kill_region(int);
180static int	x_list_comm(int);
181static int	x_list_file(int);
182static int	x_literal(int);
183static int	x_meta_yank(int);
184static int	x_mv_back(int);
185static int	x_mv_begin(int);
186static int	x_mv_bword(int);
187static int	x_mv_end(int);
188static int	x_mv_forw(int);
189static int	x_mv_fword(int);
190static int	x_newline(int);
191static int	x_next_com(int);
192static int	x_nl_next_com(int);
193static int	x_noop(int);
194static int	x_prev_com(int);
195static int	x_prev_histword(int);
196static int	x_search_char_forw(int);
197static int	x_search_char_back(int);
198static int	x_search_hist(int);
199static int	x_set_mark(int);
200static int	x_transpose(int);
201static int	x_xchg_point_mark(int);
202static int	x_yank(int);
203static int	x_comp_list(int);
204static int	x_expand(int);
205static int	x_fold_capitalize(int);
206static int	x_fold_lower(int);
207static int	x_fold_upper(int);
208static int	x_set_arg(int);
209static int	x_comment(int);
210#ifdef DEBUG
211static int	x_debug_info(int);
212#endif
213
214static const struct x_ftab x_ftab[] = {
215	{ x_abort,		"abort",			0 },
216	{ x_beg_hist,		"beginning-of-history",		0 },
217	{ x_clear_screen,	"clear-screen",			0 },
218	{ x_comp_comm,		"complete-command",		0 },
219	{ x_comp_file,		"complete-file",		0 },
220	{ x_complete,		"complete",			0 },
221	{ x_del_back,		"delete-char-backward",		XF_ARG },
222	{ x_del_bword,		"delete-word-backward",		XF_ARG },
223	{ x_del_char,		"delete-char-forward",		XF_ARG },
224	{ x_del_fword,		"delete-word-forward",		XF_ARG },
225	{ x_del_line,		"kill-line",			0 },
226	{ x_draw_line,		"redraw",			0 },
227	{ x_end_hist,		"end-of-history",		0 },
228	{ x_end_of_text,	"eot",				0 },
229	{ x_enumerate,		"list",				0 },
230	{ x_eot_del,		"eot-or-delete",		XF_ARG },
231	{ x_error,		"error",			0 },
232	{ x_goto_hist,		"goto-history",			XF_ARG },
233	{ x_ins_string,		"macro-string",			XF_NOBIND },
234	{ x_insert,		"auto-insert",			XF_ARG },
235	{ x_kill,		"kill-to-eol",			XF_ARG },
236	{ x_kill_region,	"kill-region",			0 },
237	{ x_list_comm,		"list-command",			0 },
238	{ x_list_file,		"list-file",			0 },
239	{ x_literal,		"quote",			0 },
240	{ x_meta_yank,		"yank-pop",			0 },
241	{ x_mv_back,		"backward-char",		XF_ARG },
242	{ x_mv_begin,		"beginning-of-line",		0 },
243	{ x_mv_bword,		"backward-word",		XF_ARG },
244	{ x_mv_end,		"end-of-line",			0 },
245	{ x_mv_forw,		"forward-char",			XF_ARG },
246	{ x_mv_fword,		"forward-word",			XF_ARG },
247	{ x_newline,		"newline",			0 },
248	{ x_next_com,		"down-history",			XF_ARG },
249	{ x_nl_next_com,	"newline-and-next",		0 },
250	{ x_noop,		"no-op",			0 },
251	{ x_prev_com,		"up-history",			XF_ARG },
252	{ x_prev_histword,	"prev-hist-word",		XF_ARG },
253	{ x_search_char_forw,	"search-character-forward",	XF_ARG },
254	{ x_search_char_back,	"search-character-backward",	XF_ARG },
255	{ x_search_hist,	"search-history",		0 },
256	{ x_set_mark,		"set-mark-command",		0 },
257	{ x_transpose,		"transpose-chars",		0 },
258	{ x_xchg_point_mark,	"exchange-point-and-mark",	0 },
259	{ x_yank,		"yank",				0 },
260	{ x_comp_list,		"complete-list",		0 },
261	{ x_expand,		"expand-file",			0 },
262	{ x_fold_capitalize,	"capitalize-word",		XF_ARG },
263	{ x_fold_lower,		"downcase-word",		XF_ARG },
264	{ x_fold_upper,		"upcase-word",			XF_ARG },
265	{ x_set_arg,		"set-arg",			XF_NOBIND },
266	{ x_comment,		"comment",			0 },
267	{ 0, 0, 0 },
268#ifdef DEBUG
269	{ x_debug_info,		"debug-info",			0 },
270#else
271	{ 0, 0, 0 },
272#endif
273	{ 0, 0, 0 },
274};
275
276static int
277isu8cont(unsigned char c)
278{
279	return (c & (0x80 | 0x40)) == 0x80;
280}
281
282int
283x_emacs(char *buf, size_t len)
284{
285	struct kb_entry		*k, *kmatch = NULL;
286	char			line[LINE + 1];
287	int			at = 0, ntries = 0, submatch, ret;
288	const char		*p;
289
290	xbp = xbuf = buf; xend = buf + len;
291	xlp = xcp = xep = buf;
292	*xcp = 0;
293	xlp_valid = true;
294	xmp = NULL;
295	x_histp = histptr + 1;
296
297	xx_cols = x_cols;
298	x_col = promptlen(prompt, &p);
299	prompt_skip = p - prompt;
300	x_adj_ok = 1;
301	prompt_redraw = 1;
302	if (x_col > xx_cols)
303		x_col = x_col - (x_col / xx_cols) * xx_cols;
304	x_displen = xx_cols - 2 - x_col;
305	x_adj_done = 0;
306
307	pprompt(prompt, 0);
308	if (x_displen < 1) {
309		x_col = 0;
310		x_displen = xx_cols - 2;
311		x_e_putc('\n');
312		prompt_redraw = 0;
313	}
314
315	if (x_nextcmd >= 0) {
316		int off = source->line - x_nextcmd;
317		if (histptr - history >= off)
318			x_load_hist(histptr - off);
319		x_nextcmd = -1;
320	}
321
322	x_literal_set = 0;
323	x_arg = -1;
324	x_last_command = NULL;
325	while (1) {
326		x_flush();
327		if ((at = x_e_getu8(line, at)) < 0)
328			return 0;
329		ntries++;
330
331		if (x_arg == -1) {
332			x_arg = 1;
333			x_arg_defaulted = 1;
334		}
335
336		if (x_literal_set) {
337			/* literal, so insert it */
338			x_literal_set = 0;
339			submatch = 0;
340		} else {
341			submatch = 0;
342			kmatch = NULL;
343			TAILQ_FOREACH(k, &kblist, entry) {
344				if (at > k->len)
345					continue;
346
347				if (memcmp(k->seq, line, at) == 0) {
348					/* sub match */
349					submatch++;
350					if (k->len == at)
351						kmatch = k;
352				}
353
354				/* see if we can abort search early */
355				if (submatch > 1)
356					break;
357			}
358		}
359
360		if (submatch == 1 && kmatch) {
361			if (kmatch->ftab->xf_func == x_ins_string &&
362			    kmatch->args && !macro_args) {
363				/* treat macro string as input */
364				macro_args = kmatch->args;
365				ret = KSTD;
366			} else
367				ret = kmatch->ftab->xf_func(line[at - 1]);
368		} else {
369			if (submatch)
370				continue;
371			if (ntries > 1) {
372				ret = x_error(0); /* unmatched meta sequence */
373			} else if (at > 1) {
374				x_ins(line);
375				ret = KSTD;
376			} else {
377				ret = x_insert(line[0]);
378			}
379		}
380
381		switch (ret) {
382		case KSTD:
383			if (kmatch)
384				x_last_command = kmatch->ftab->xf_func;
385			else
386				x_last_command = NULL;
387			break;
388		case KEOL:
389			ret = xep - xbuf;
390			return (ret);
391			break;
392		case KINTR:
393			trapsig(SIGINT);
394			x_mode(false);
395			unwind(LSHELL);
396			x_arg = -1;
397			break;
398		default:
399			bi_errorf("invalid return code"); /* can't happen */
400		}
401
402		/* reset meta sequence */
403		at = ntries = 0;
404		if (x_arg_set)
405			x_arg_set = 0; /* reset args next time around */
406		else
407			x_arg = -1;
408	}
409}
410
411static int
412x_insert(int c)
413{
414	char	str[2];
415
416	/*
417	 *  Should allow tab and control chars.
418	 */
419	if (c == 0) {
420		x_e_putc(BEL);
421		return KSTD;
422	}
423	str[0] = c;
424	str[1] = '\0';
425	while (x_arg--)
426		x_ins(str);
427	return KSTD;
428}
429
430static int
431x_ins_string(int c)
432{
433	return x_insert(c);
434}
435
436static int
437x_do_ins(const char *cp, size_t len)
438{
439	if (xep+len >= xend) {
440		x_e_putc(BEL);
441		return -1;
442	}
443
444	memmove(xcp+len, xcp, xep - xcp + 1);
445	memmove(xcp, cp, len);
446	xcp += len;
447	xep += len;
448	return 0;
449}
450
451static int
452x_ins(char *s)
453{
454	char	*cp = xcp;
455	int	adj = x_adj_done;
456
457	if (x_do_ins(s, strlen(s)) < 0)
458		return -1;
459	/*
460	 * x_zots() may result in a call to x_adjust()
461	 * we want xcp to reflect the new position.
462	 */
463	xlp_valid = false;
464	x_lastcp();
465	x_adj_ok = (xcp >= xlp);
466	x_zots(cp);
467	if (adj == x_adj_done) {	/* has x_adjust() been called? */
468		/* no */
469		for (cp = xlp; cp > xcp; )
470			x_bs(*--cp);
471	}
472
473	x_adj_ok = 1;
474	return 0;
475}
476
477static int
478x_del_back(int c)
479{
480	int col = xcp - xbuf;
481
482	if (col == 0) {
483		x_e_putc(BEL);
484		return KSTD;
485	}
486	if (x_arg > col)
487		x_arg = col;
488	while (x_arg < col && isu8cont(xcp[-x_arg]))
489		x_arg++;
490	x_goto(xcp - x_arg);
491	x_delete(x_arg, false);
492	return KSTD;
493}
494
495static int
496x_del_char(int c)
497{
498	int nleft = xep - xcp;
499
500	if (!nleft) {
501		x_e_putc(BEL);
502		return KSTD;
503	}
504	if (x_arg > nleft)
505		x_arg = nleft;
506	while (x_arg < nleft && isu8cont(xcp[x_arg]))
507		x_arg++;
508	x_delete(x_arg, false);
509	return KSTD;
510}
511
512/* Delete nc bytes to the right of the cursor (including cursor position) */
513static void
514x_delete(int nc, int push)
515{
516	int	i,j;
517	char	*cp;
518
519	if (nc == 0)
520		return;
521	if (xmp != NULL && xmp > xcp) {
522		if (xcp + nc > xmp)
523			xmp = xcp;
524		else
525			xmp -= nc;
526	}
527
528	/*
529	 * This lets us yank a word we have deleted.
530	 */
531	if (push)
532		x_push(nc);
533
534	xep -= nc;
535	cp = xcp;
536	j = 0;
537	i = nc;
538	while (i--) {
539		j += x_size((unsigned char)*cp++);
540	}
541	memmove(xcp, xcp+nc, xep - xcp + 1);	/* Copies the null */
542	x_adj_ok = 0;			/* don't redraw */
543	xlp_valid = false;
544	x_zots(xcp);
545	/*
546	 * if we are already filling the line,
547	 * there is no need to ' ','\b'.
548	 * But if we must, make sure we do the minimum.
549	 */
550	if ((i = xx_cols - 2 - x_col) > 0) {
551		j = (j < i) ? j : i;
552		i = j;
553		while (i--)
554			x_e_putc(' ');
555		i = j;
556		while (i--)
557			x_e_putc('\b');
558	}
559	/*x_goto(xcp);*/
560	x_adj_ok = 1;
561	xlp_valid = false;
562	for (cp = x_lastcp(); cp > xcp; )
563		x_bs(*--cp);
564
565	return;
566}
567
568static int
569x_del_bword(int c)
570{
571	x_delete(x_bword(), true);
572	return KSTD;
573}
574
575static int
576x_mv_bword(int c)
577{
578	(void)x_bword();
579	return KSTD;
580}
581
582static int
583x_mv_fword(int c)
584{
585	x_goto(xcp + x_fword());
586	return KSTD;
587}
588
589static int
590x_del_fword(int c)
591{
592	x_delete(x_fword(), true);
593	return KSTD;
594}
595
596static int
597x_bword(void)
598{
599	int	nc = 0;
600	char	*cp = xcp;
601
602	if (cp == xbuf) {
603		x_e_putc(BEL);
604		return 0;
605	}
606	while (x_arg--) {
607		while (cp != xbuf && is_mfs(cp[-1])) {
608			cp--;
609			nc++;
610		}
611		while (cp != xbuf && !is_mfs(cp[-1])) {
612			cp--;
613			nc++;
614		}
615	}
616	x_goto(cp);
617	return nc;
618}
619
620static int
621x_fword(void)
622{
623	int	nc = 0;
624	char	*cp = xcp;
625
626	if (cp == xep) {
627		x_e_putc(BEL);
628		return 0;
629	}
630	while (x_arg--) {
631		while (cp != xep && is_mfs(*cp)) {
632			cp++;
633			nc++;
634		}
635		while (cp != xep && !is_mfs(*cp)) {
636			cp++;
637			nc++;
638		}
639	}
640	return nc;
641}
642
643static void
644x_goto(char *cp)
645{
646	if (cp < xbp || cp >= (xbp + x_displen)) {
647		/* we are heading off screen */
648		xcp = cp;
649		x_adjust();
650	} else if (cp < xcp) {		/* move back */
651		while (cp < xcp)
652			x_bs((unsigned char)*--xcp);
653	} else if (cp > xcp) {		/* move forward */
654		while (cp > xcp)
655			x_zotc((unsigned char)*xcp++);
656	}
657}
658
659static void
660x_bs(int c)
661{
662	int i;
663
664	i = x_size(c);
665	while (i--)
666		x_e_putc('\b');
667}
668
669static int
670x_size_str(char *cp)
671{
672	int size = 0;
673	while (*cp)
674		size += x_size(*cp++);
675	return size;
676}
677
678static int
679x_size(int c)
680{
681	if (c=='\t')
682		return 4;	/* Kludge, tabs are always four spaces. */
683	if (iscntrl(c))		/* control char */
684		return 2;
685	if (isu8cont(c))
686		return 0;
687	return 1;
688}
689
690static void
691x_zots(char *str)
692{
693	int	adj = x_adj_done;
694
695	if (str > xbuf && isu8cont(*str)) {
696		while (str > xbuf && isu8cont(*str))
697			str--;
698		x_e_putc('\b');
699	}
700	x_lastcp();
701	while (*str && str < xlp && adj == x_adj_done)
702		x_zotc(*str++);
703}
704
705static void
706x_zotc(int c)
707{
708	if (c == '\t') {
709		/*  Kludge, tabs are always four spaces.  */
710		x_e_puts("    ");
711	} else if (iscntrl(c)) {
712		x_e_putc('^');
713		x_e_putc(UNCTRL(c));
714	} else
715		x_e_putc(c);
716}
717
718static int
719x_mv_back(int c)
720{
721	int col = xcp - xbuf;
722
723	if (col == 0) {
724		x_e_putc(BEL);
725		return KSTD;
726	}
727	if (x_arg > col)
728		x_arg = col;
729	while (x_arg < col && isu8cont(xcp[-x_arg]))
730		x_arg++;
731	x_goto(xcp - x_arg);
732	return KSTD;
733}
734
735static int
736x_mv_forw(int c)
737{
738	int nleft = xep - xcp;
739
740	if (!nleft) {
741		x_e_putc(BEL);
742		return KSTD;
743	}
744	if (x_arg > nleft)
745		x_arg = nleft;
746	while (x_arg < nleft && isu8cont(xcp[x_arg]))
747		x_arg++;
748	x_goto(xcp + x_arg);
749	return KSTD;
750}
751
752static int
753x_search_char_forw(int c)
754{
755	char *cp = xcp;
756
757	*xep = '\0';
758	c = x_e_getc();
759	while (x_arg--) {
760		if (c < 0 ||
761		    ((cp = (cp == xep) ? NULL : strchr(cp + 1, c)) == NULL &&
762		    (cp = strchr(xbuf, c)) == NULL)) {
763			x_e_putc(BEL);
764			return KSTD;
765		}
766	}
767	x_goto(cp);
768	return KSTD;
769}
770
771static int
772x_search_char_back(int c)
773{
774	char *cp = xcp, *p;
775
776	c = x_e_getc();
777	for (; x_arg--; cp = p)
778		for (p = cp; ; ) {
779			if (p-- == xbuf)
780				p = xep;
781			if (c < 0 || p == cp) {
782				x_e_putc(BEL);
783				return KSTD;
784			}
785			if (*p == c)
786				break;
787		}
788	x_goto(cp);
789	return KSTD;
790}
791
792static int
793x_newline(int c)
794{
795	x_e_putc('\r');
796	x_e_putc('\n');
797	x_flush();
798	*xep++ = '\n';
799	return KEOL;
800}
801
802static int
803x_end_of_text(int c)
804{
805	x_zotc(edchars.eof);
806	x_putc('\r');
807	x_putc('\n');
808	x_flush();
809	return KEOL;
810}
811
812static int x_beg_hist(int c) { x_load_hist(history); return KSTD;}
813
814static int x_end_hist(int c) { x_load_hist(histptr); return KSTD;}
815
816static int x_prev_com(int c) { x_load_hist(x_histp - x_arg); return KSTD;}
817
818static int x_next_com(int c) { x_load_hist(x_histp + x_arg); return KSTD;}
819
820/* Goto a particular history number obtained from argument.
821 * If no argument is given history 1 is probably not what you
822 * want so we'll simply go to the oldest one.
823 */
824static int
825x_goto_hist(int c)
826{
827	if (x_arg_defaulted)
828		x_load_hist(history);
829	else
830		x_load_hist(histptr + x_arg - source->line);
831	return KSTD;
832}
833
834static void
835x_load_hist(char **hp)
836{
837	int	oldsize;
838
839	if (hp < history || hp > histptr) {
840		x_e_putc(BEL);
841		return;
842	}
843	x_histp = hp;
844	oldsize = x_size_str(xbuf);
845	strlcpy(xbuf, *hp, xend - xbuf);
846	xbp = xbuf;
847	xep = xcp = xbuf + strlen(xbuf);
848	xlp_valid = false;
849	if (xep <= x_lastcp())
850		x_redraw(oldsize);
851	x_goto(xep);
852}
853
854static int
855x_nl_next_com(int c)
856{
857	x_nextcmd = source->line - (histptr - x_histp) + 1;
858	return (x_newline(c));
859}
860
861static int
862x_eot_del(int c)
863{
864	if (xep == xbuf && x_arg_defaulted)
865		return (x_end_of_text(c));
866	else
867		return (x_del_char(c));
868}
869
870static kb_func
871kb_find_hist_func(char c)
872{
873	struct kb_entry		*k;
874	char			line[LINE + 1];
875
876	line[0] = c;
877	line[1] = '\0';
878	TAILQ_FOREACH(k, &kblist, entry)
879		if (!strcmp(k->seq, line))
880			return (k->ftab->xf_func);
881
882	return (x_insert);
883}
884
885/* reverse incremental history search */
886static int
887x_search_hist(int c)
888{
889	int offset = -1;	/* offset of match in xbuf, else -1 */
890	char pat [256+1];	/* pattern buffer */
891	char *p = pat;
892	int (*f)(int);
893
894	*p = '\0';
895	while (1) {
896		if (offset < 0) {
897			x_e_puts("\nI-search: ");
898			x_e_puts(pat);
899		}
900		x_flush();
901		if ((c = x_e_getc()) < 0)
902			return KSTD;
903		f = kb_find_hist_func(c);
904		if (c == CTRL('[') || c == CTRL('@')) {
905			x_e_ungetc(c);
906			break;
907		} else if (f == x_search_hist)
908			offset = x_search(pat, 0, offset);
909		else if (f == x_del_back) {
910			if (p == pat) {
911				offset = -1;
912				break;
913			}
914			if (p > pat)
915				*--p = '\0';
916			if (p == pat)
917				offset = -1;
918			else
919				offset = x_search(pat, 1, offset);
920			continue;
921		} else if (f == x_insert) {
922			/* add char to pattern */
923			/* overflow check... */
924			if (p >= &pat[sizeof(pat) - 1]) {
925				x_e_putc(BEL);
926				continue;
927			}
928			*p++ = c, *p = '\0';
929			if (offset >= 0) {
930				/* already have partial match */
931				offset = x_match(xbuf, pat);
932				if (offset >= 0) {
933					x_goto(xbuf + offset + (p - pat) -
934					    (*pat == '^'));
935					continue;
936				}
937			}
938			offset = x_search(pat, 0, offset);
939		} else { /* other command */
940			x_e_ungetc(c);
941			break;
942		}
943	}
944	if (offset < 0)
945		x_redraw(-1);
946	return KSTD;
947}
948
949/* search backward from current line */
950static int
951x_search(char *pat, int sameline, int offset)
952{
953	char **hp;
954	int i;
955
956	for (hp = x_histp - (sameline ? 0 : 1) ; hp >= history; --hp) {
957		i = x_match(*hp, pat);
958		if (i >= 0) {
959			if (offset < 0)
960				x_e_putc('\n');
961			x_load_hist(hp);
962			x_goto(xbuf + i + strlen(pat) - (*pat == '^'));
963			return i;
964		}
965	}
966	x_e_putc(BEL);
967	x_histp = histptr;
968	return -1;
969}
970
971/* return position of first match of pattern in string, else -1 */
972static int
973x_match(char *str, char *pat)
974{
975	if (*pat == '^') {
976		return (strncmp(str, pat+1, strlen(pat+1)) == 0) ? 0 : -1;
977	} else {
978		char *q = strstr(str, pat);
979		return (q == NULL) ? -1 : q - str;
980	}
981}
982
983static int
984x_del_line(int c)
985{
986	int	i, j;
987
988	*xep = 0;
989	i = xep - xbuf;
990	j = x_size_str(xbuf);
991	xcp = xbuf;
992	x_push(i);
993	xlp = xbp = xep = xbuf;
994	xlp_valid = true;
995	*xcp = 0;
996	xmp = NULL;
997	x_redraw(j);
998	return KSTD;
999}
1000
1001static int
1002x_mv_end(int c)
1003{
1004	x_goto(xep);
1005	return KSTD;
1006}
1007
1008static int
1009x_mv_begin(int c)
1010{
1011	x_goto(xbuf);
1012	return KSTD;
1013}
1014
1015static int
1016x_draw_line(int c)
1017{
1018	x_redraw(-1);
1019	return KSTD;
1020}
1021
1022static int
1023x_clear_screen(int c)
1024{
1025	x_redraw(-2);
1026	return KSTD;
1027}
1028
1029/* Redraw (part of) the line.
1030 * A non-negative limit is the screen column up to which needs
1031 * redrawing. A limit of -1 redraws on a new line, while a limit
1032 * of -2 (attempts to) clear the screen.
1033 */
1034static void
1035x_redraw(int limit)
1036{
1037	int	i, j, truncate = 0;
1038	char	*cp;
1039
1040	x_adj_ok = 0;
1041	if (limit == -2) {
1042		int cleared = 0;
1043#if !defined(SMALL) && !defined(NO_CURSES)
1044		if (cur_term != NULL && clear_screen != NULL) {
1045			if (tputs(clear_screen, 1, x_putc) != ERR)
1046				cleared = 1;
1047		}
1048#endif
1049		if (!cleared)
1050			x_e_putc('\n');
1051	}
1052	else if (limit == -1)
1053		x_e_putc('\n');
1054	else if (limit >= 0)
1055		x_e_putc('\r');
1056	x_flush();
1057	if (xbp == xbuf) {
1058		x_col = promptlen(prompt, NULL);
1059		if (x_col > xx_cols)
1060			truncate = (x_col / xx_cols) * xx_cols;
1061		if (prompt_redraw)
1062			pprompt(prompt + prompt_skip, truncate);
1063	}
1064	if (x_col > xx_cols)
1065		x_col = x_col - (x_col / xx_cols) * xx_cols;
1066	x_displen = xx_cols - 2 - x_col;
1067	if (x_displen < 1) {
1068		x_col = 0;
1069		x_displen = xx_cols - 2;
1070	}
1071	xlp_valid = false;
1072	x_lastcp();
1073	x_zots(xbp);
1074	if (xbp != xbuf || xep > xlp)
1075		limit = xx_cols;
1076	if (limit >= 0) {
1077		if (xep > xlp)
1078			i = 0;			/* we fill the line */
1079		else
1080			i = limit - (xlp - xbp);
1081
1082		for (j = 0; j < i && x_col < (xx_cols - 2); j++)
1083			x_e_putc(' ');
1084		i = ' ';
1085		if (xep > xlp) {		/* more off screen */
1086			if (xbp > xbuf)
1087				i = '*';
1088			else
1089				i = '>';
1090		} else if (xbp > xbuf)
1091			i = '<';
1092		x_e_putc(i);
1093		j++;
1094		while (j--)
1095			x_e_putc('\b');
1096	}
1097	for (cp = xlp; cp > xcp; )
1098		x_bs(*--cp);
1099	x_adj_ok = 1;
1100#ifdef DEBUG
1101	x_flush();
1102#endif
1103	return;
1104}
1105
1106static int
1107x_transpose(int c)
1108{
1109	char	tmp;
1110
1111	/* What transpose is meant to do seems to be up for debate. This
1112	 * is a general summary of the options; the text is abcd with the
1113	 * upper case character or underscore indicating the cursor position:
1114	 *     Who			Before	After  Before	After
1115	 *     at&t ksh in emacs mode:	abCd	abdC   abcd_	(bell)
1116	 *     at&t ksh in gmacs mode:	abCd	baCd   abcd_	abdc_
1117	 *     gnu emacs:		abCd	acbD   abcd_	abdc_
1118	 * Pdksh currently goes with GNU behavior since I believe this is the
1119	 * most common version of emacs, unless in gmacs mode, in which case
1120	 * it does the at&t ksh gmacs mode.
1121	 * This should really be broken up into 3 functions so users can bind
1122	 * to the one they want.
1123	 */
1124	if (xcp == xbuf) {
1125		x_e_putc(BEL);
1126		return KSTD;
1127	} else if (xcp == xep || Flag(FGMACS)) {
1128		if (xcp - xbuf == 1) {
1129			x_e_putc(BEL);
1130			return KSTD;
1131		}
1132		/* Gosling/Unipress emacs style: Swap two characters before the
1133		 * cursor, do not change cursor position
1134		 */
1135		x_bs(xcp[-1]);
1136		x_bs(xcp[-2]);
1137		x_zotc(xcp[-1]);
1138		x_zotc(xcp[-2]);
1139		tmp = xcp[-1];
1140		xcp[-1] = xcp[-2];
1141		xcp[-2] = tmp;
1142	} else {
1143		/* GNU emacs style: Swap the characters before and under the
1144		 * cursor, move cursor position along one.
1145		 */
1146		x_bs(xcp[-1]);
1147		x_zotc(xcp[0]);
1148		x_zotc(xcp[-1]);
1149		tmp = xcp[-1];
1150		xcp[-1] = xcp[0];
1151		xcp[0] = tmp;
1152		x_bs(xcp[0]);
1153		x_goto(xcp + 1);
1154	}
1155	return KSTD;
1156}
1157
1158static int
1159x_literal(int c)
1160{
1161	x_literal_set = 1;
1162	return KSTD;
1163}
1164
1165static int
1166x_kill(int c)
1167{
1168	int col = xcp - xbuf;
1169	int lastcol = xep - xbuf;
1170	int ndel;
1171
1172	if (x_arg_defaulted)
1173		x_arg = lastcol;
1174	else if (x_arg > lastcol)
1175		x_arg = lastcol;
1176	while (x_arg < lastcol && isu8cont(xbuf[x_arg]))
1177		x_arg++;
1178	ndel = x_arg - col;
1179	if (ndel < 0) {
1180		x_goto(xbuf + x_arg);
1181		ndel = -ndel;
1182	}
1183	x_delete(ndel, true);
1184	return KSTD;
1185}
1186
1187static void
1188x_push(int nchars)
1189{
1190	char	*cp = str_nsave(xcp, nchars, AEDIT);
1191	afree(killstack[killsp], AEDIT);
1192	killstack[killsp] = cp;
1193	killsp = (killsp + 1) % KILLSIZE;
1194}
1195
1196static int
1197x_yank(int c)
1198{
1199	if (killsp == 0)
1200		killtp = KILLSIZE;
1201	else
1202		killtp = killsp;
1203	killtp --;
1204	if (killstack[killtp] == 0) {
1205		x_e_puts("\nnothing to yank");
1206		x_redraw(-1);
1207		return KSTD;
1208	}
1209	xmp = xcp;
1210	x_ins(killstack[killtp]);
1211	return KSTD;
1212}
1213
1214static int
1215x_meta_yank(int c)
1216{
1217	int	len;
1218	if ((x_last_command != x_yank && x_last_command != x_meta_yank) ||
1219	    killstack[killtp] == 0) {
1220		killtp = killsp;
1221		x_e_puts("\nyank something first");
1222		x_redraw(-1);
1223		return KSTD;
1224	}
1225	len = strlen(killstack[killtp]);
1226	x_goto(xcp - len);
1227	x_delete(len, false);
1228	do {
1229		if (killtp == 0)
1230			killtp = KILLSIZE - 1;
1231		else
1232			killtp--;
1233	} while (killstack[killtp] == 0);
1234	x_ins(killstack[killtp]);
1235	return KSTD;
1236}
1237
1238static int
1239x_abort(int c)
1240{
1241	/* x_zotc(c); */
1242	xlp = xep = xcp = xbp = xbuf;
1243	xlp_valid = true;
1244	*xcp = 0;
1245	return KINTR;
1246}
1247
1248static int
1249x_error(int c)
1250{
1251	x_e_putc(BEL);
1252	return KSTD;
1253}
1254
1255static char *
1256kb_encode(const char *s)
1257{
1258	static char		l[LINE + 1];
1259	int			at = 0;
1260
1261	l[at] = '\0';
1262	while (*s) {
1263		if (*s == '^') {
1264			s++;
1265			if (*s >= '?')
1266				l[at++] = CTRL(*s);
1267			else {
1268				l[at++] = '^';
1269				s--;
1270			}
1271		} else
1272			l[at++] = *s;
1273		l[at] = '\0';
1274		s++;
1275	}
1276	return (l);
1277}
1278
1279static char *
1280kb_decode(const char *s)
1281{
1282	static char		l[LINE + 1];
1283	unsigned int		i, at = 0;
1284
1285	l[0] = '\0';
1286	for (i = 0; i < strlen(s); i++) {
1287		if (iscntrl((unsigned char)s[i])) {
1288			l[at++] = '^';
1289			l[at++] = UNCTRL(s[i]);
1290		} else
1291			l[at++] = s[i];
1292		l[at] = '\0';
1293	}
1294
1295	return (l);
1296}
1297
1298static int
1299kb_match(char *s)
1300{
1301	int			len = strlen(s);
1302	struct kb_entry		*k;
1303
1304	TAILQ_FOREACH(k, &kblist, entry) {
1305		if (len > k->len)
1306			continue;
1307
1308		if (memcmp(k->seq, s, len) == 0)
1309			return (1);
1310	}
1311
1312	return (0);
1313}
1314
1315static void
1316kb_del(struct kb_entry *k)
1317{
1318	TAILQ_REMOVE(&kblist, k, entry);
1319	free(k->args);
1320	afree(k, AEDIT);
1321}
1322
1323static struct kb_entry *
1324kb_add_string(kb_func func, void *args, char *str)
1325{
1326	unsigned int		ele, count;
1327	struct kb_entry		*k;
1328	struct x_ftab		*xf = NULL;
1329
1330	for (ele = 0; ele < NELEM(x_ftab); ele++)
1331		if (x_ftab[ele].xf_func == func) {
1332			xf = (struct x_ftab *)&x_ftab[ele];
1333			break;
1334		}
1335	if (xf == NULL)
1336		return (NULL);
1337
1338	if (kb_match(str)) {
1339		if (x_bind_quiet == 0)
1340			bi_errorf("duplicate binding for %s", kb_decode(str));
1341		return (NULL);
1342	}
1343	count = strlen(str);
1344
1345	k = alloc(sizeof *k + count + 1, AEDIT);
1346	k->seq = (unsigned char *)(k + 1);
1347	k->len = count;
1348	k->ftab = xf;
1349	k->args = args ? strdup(args) : NULL;
1350
1351	strlcpy(k->seq, str, count + 1);
1352
1353	TAILQ_INSERT_TAIL(&kblist, k, entry);
1354
1355	return (k);
1356}
1357
1358static struct kb_entry *
1359kb_add(kb_func func, ...)
1360{
1361	va_list			ap;
1362	unsigned char		ch;
1363	unsigned int		i;
1364	char			line[LINE + 1];
1365
1366	va_start(ap, func);
1367	for (i = 0; i < sizeof(line) - 1; i++) {
1368		ch = va_arg(ap, unsigned int);
1369		if (ch == 0)
1370			break;
1371		line[i] = ch;
1372	}
1373	va_end(ap);
1374	line[i] = '\0';
1375
1376	return (kb_add_string(func, NULL, line));
1377}
1378
1379static void
1380kb_print(struct kb_entry *k)
1381{
1382	if (!(k->ftab->xf_flags & XF_NOBIND))
1383		shprintf("%s = %s\n",
1384		    kb_decode(k->seq), k->ftab->xf_name);
1385	else if (k->args) {
1386		shprintf("%s = ", kb_decode(k->seq));
1387		shprintf("'%s'\n", kb_decode(k->args));
1388	}
1389}
1390
1391int
1392x_bind(const char *a1, const char *a2,
1393	int macro,		/* bind -m */
1394	int list)		/* bind -l */
1395{
1396	unsigned int		i;
1397	struct kb_entry		*k, *kb;
1398	char			in[LINE + 1];
1399
1400	if (x_tty == 0) {
1401		bi_errorf("cannot bind, not a tty");
1402		return (1);
1403	}
1404
1405	if (list) {
1406		/* show all function names */
1407		for (i = 0; i < NELEM(x_ftab); i++) {
1408			if (x_ftab[i].xf_name == NULL)
1409				continue;
1410			if (x_ftab[i].xf_name &&
1411			    !(x_ftab[i].xf_flags & XF_NOBIND))
1412				shprintf("%s\n", x_ftab[i].xf_name);
1413		}
1414		return (0);
1415	}
1416
1417	if (a1 == NULL) {
1418		/* show all bindings */
1419		TAILQ_FOREACH(k, &kblist, entry)
1420			kb_print(k);
1421		return (0);
1422	}
1423
1424	snprintf(in, sizeof in, "%s", kb_encode(a1));
1425	if (a2 == NULL) {
1426		/* print binding */
1427		TAILQ_FOREACH(k, &kblist, entry)
1428			if (!strcmp(k->seq, in)) {
1429				kb_print(k);
1430				return (0);
1431			}
1432		shprintf("%s = %s\n", kb_decode(a1), "auto-insert");
1433		return (0);
1434	}
1435
1436	if (strlen(a2) == 0) {
1437		/* clear binding */
1438		TAILQ_FOREACH_SAFE(k, &kblist, entry, kb)
1439			if (!strcmp(k->seq, in)) {
1440				kb_del(k);
1441				break;
1442			}
1443		return (0);
1444	}
1445
1446	/* set binding */
1447	if (macro) {
1448		/* delete old mapping */
1449		TAILQ_FOREACH_SAFE(k, &kblist, entry, kb)
1450			if (!strcmp(k->seq, in)) {
1451				kb_del(k);
1452				break;
1453			}
1454		kb_add_string(x_ins_string, kb_encode(a2), in);
1455		return (0);
1456	}
1457
1458	/* set non macro binding */
1459	for (i = 0; i < NELEM(x_ftab); i++) {
1460		if (x_ftab[i].xf_name == NULL)
1461			continue;
1462		if (!strcmp(x_ftab[i].xf_name, a2)) {
1463			/* delete old mapping */
1464			TAILQ_FOREACH_SAFE(k, &kblist, entry, kb)
1465				if (!strcmp(k->seq, in)) {
1466					kb_del(k);
1467					break;
1468				}
1469			kb_add_string(x_ftab[i].xf_func, NULL, in);
1470			return (0);
1471		}
1472	}
1473	bi_errorf("%s: no such function", a2);
1474	return (1);
1475}
1476
1477void
1478x_init_emacs(void)
1479{
1480	x_tty = 1;
1481	ainit(AEDIT);
1482	x_nextcmd = -1;
1483
1484	TAILQ_INIT(&kblist);
1485
1486	/* man page order */
1487	kb_add(x_abort,			CTRL('G'), 0);
1488	kb_add(x_mv_back,		CTRL('B'), 0);
1489	kb_add(x_mv_back,		CTRL('X'), CTRL('D'), 0);
1490	kb_add(x_mv_bword,		CTRL('['), 'b', 0);
1491	kb_add(x_beg_hist,		CTRL('['), '<', 0);
1492	kb_add(x_mv_begin,		CTRL('A'), 0);
1493	kb_add(x_fold_capitalize,	CTRL('['), 'C', 0);
1494	kb_add(x_fold_capitalize,	CTRL('['), 'c', 0);
1495	kb_add(x_comment,		CTRL('['), '#', 0);
1496	kb_add(x_complete,		CTRL('['), CTRL('['), 0);
1497	kb_add(x_comp_comm,		CTRL('X'), CTRL('['), 0);
1498	kb_add(x_comp_file,		CTRL('['), CTRL('X'), 0);
1499	kb_add(x_comp_list,		CTRL('I'), 0);
1500	kb_add(x_comp_list,		CTRL('['), '=', 0);
1501	kb_add(x_del_back,		CTRL('?'), 0);
1502	kb_add(x_del_back,		CTRL('H'), 0);
1503	kb_add(x_del_char,		CTRL('['), '[', '3', '~', 0); /* delete */
1504	kb_add(x_del_bword,		CTRL('W'), 0);
1505	kb_add(x_del_bword,		CTRL('['), CTRL('?'), 0);
1506	kb_add(x_del_bword,		CTRL('['), CTRL('H'), 0);
1507	kb_add(x_del_bword,		CTRL('['), 'h', 0);
1508	kb_add(x_del_fword,		CTRL('['), 'd', 0);
1509	kb_add(x_next_com,		CTRL('N'), 0);
1510	kb_add(x_next_com,		CTRL('X'), 'B', 0);
1511	kb_add(x_fold_lower,		CTRL('['), 'L', 0);
1512	kb_add(x_fold_lower,		CTRL('['), 'l', 0);
1513	kb_add(x_end_hist,		CTRL('['), '>', 0);
1514	kb_add(x_mv_end,		CTRL('E'), 0);
1515	/* how to handle: eot: ^_, underneath copied from original keybindings */
1516	kb_add(x_end_of_text,		CTRL('_'), 0);
1517	kb_add(x_eot_del,		CTRL('D'), 0);
1518	/* error */
1519	kb_add(x_xchg_point_mark,	CTRL('X'), CTRL('X'), 0);
1520	kb_add(x_expand,		CTRL('['), '*', 0);
1521	kb_add(x_mv_forw,		CTRL('F'), 0);
1522	kb_add(x_mv_forw,		CTRL('X'), 'C', 0);
1523	kb_add(x_mv_fword,		CTRL('['), 'f', 0);
1524	kb_add(x_goto_hist,		CTRL('['), 'g', 0);
1525	/* kill-line */
1526	kb_add(x_kill,			CTRL('K'), 0);
1527	kb_add(x_enumerate,		CTRL('['), '?', 0);
1528	kb_add(x_list_comm,		CTRL('X'), '?', 0);
1529	kb_add(x_list_file,		CTRL('X'), CTRL('Y'), 0);
1530	kb_add(x_newline,		CTRL('J'), 0);
1531	kb_add(x_newline,		CTRL('M'), 0);
1532	kb_add(x_nl_next_com,		CTRL('O'), 0);
1533	/* no-op */
1534	kb_add(x_prev_histword,		CTRL('['), '.', 0);
1535	kb_add(x_prev_histword,		CTRL('['), '_', 0);
1536	/* how to handle: quote: ^^ */
1537	kb_add(x_literal,		CTRL('^'), 0);
1538	kb_add(x_clear_screen,		CTRL('L'), 0);
1539	kb_add(x_search_char_back,	CTRL('['), CTRL(']'), 0);
1540	kb_add(x_search_char_forw,	CTRL(']'), 0);
1541	kb_add(x_search_hist,		CTRL('R'), 0);
1542	kb_add(x_set_mark,		CTRL('['), ' ', 0);
1543	kb_add(x_transpose,		CTRL('T'), 0);
1544	kb_add(x_prev_com,		CTRL('P'), 0);
1545	kb_add(x_prev_com,		CTRL('X'), 'A', 0);
1546	kb_add(x_fold_upper,		CTRL('['), 'U', 0);
1547	kb_add(x_fold_upper,		CTRL('['), 'u', 0);
1548	kb_add(x_literal,		CTRL('V'), 0);
1549	kb_add(x_yank,			CTRL('Y'), 0);
1550	kb_add(x_meta_yank,		CTRL('['), 'y', 0);
1551	/* man page ends here */
1552
1553	/* arrow keys */
1554	kb_add(x_prev_com,		CTRL('['), '[', 'A', 0); /* up */
1555	kb_add(x_next_com,		CTRL('['), '[', 'B', 0); /* down */
1556	kb_add(x_mv_forw,		CTRL('['), '[', 'C', 0); /* right */
1557	kb_add(x_mv_back,		CTRL('['), '[', 'D', 0); /* left */
1558	kb_add(x_prev_com,		CTRL('['), 'O', 'A', 0); /* up */
1559	kb_add(x_next_com,		CTRL('['), 'O', 'B', 0); /* down */
1560	kb_add(x_mv_forw,		CTRL('['), 'O', 'C', 0); /* right */
1561	kb_add(x_mv_back,		CTRL('['), 'O', 'D', 0); /* left */
1562
1563	/* more navigation keys */
1564	kb_add(x_mv_begin,		CTRL('['), '[', 'H', 0); /* home */
1565	kb_add(x_mv_end,		CTRL('['), '[', 'F', 0); /* end */
1566	kb_add(x_mv_begin,		CTRL('['), 'O', 'H', 0); /* home */
1567	kb_add(x_mv_end,		CTRL('['), 'O', 'F', 0); /* end */
1568	kb_add(x_mv_begin,		CTRL('['), '[', '1', '~', 0); /* home */
1569	kb_add(x_mv_end,		CTRL('['), '[', '4', '~', 0); /* end */
1570	kb_add(x_mv_begin,		CTRL('['), '[', '7', '~', 0); /* home */
1571	kb_add(x_mv_end,		CTRL('['), '[', '8', '~', 0); /* end */
1572
1573	/* can't be bound */
1574	kb_add(x_set_arg,		CTRL('['), '0', 0);
1575	kb_add(x_set_arg,		CTRL('['), '1', 0);
1576	kb_add(x_set_arg,		CTRL('['), '2', 0);
1577	kb_add(x_set_arg,		CTRL('['), '3', 0);
1578	kb_add(x_set_arg,		CTRL('['), '4', 0);
1579	kb_add(x_set_arg,		CTRL('['), '5', 0);
1580	kb_add(x_set_arg,		CTRL('['), '6', 0);
1581	kb_add(x_set_arg,		CTRL('['), '7', 0);
1582	kb_add(x_set_arg,		CTRL('['), '8', 0);
1583	kb_add(x_set_arg,		CTRL('['), '9', 0);
1584
1585	/* ctrl arrow keys */
1586	kb_add(x_mv_end,		CTRL('['), '[', '1', ';', '5', 'A', 0); /* ctrl up */
1587	kb_add(x_mv_begin,		CTRL('['), '[', '1', ';', '5', 'B', 0); /* ctrl down */
1588	kb_add(x_mv_fword,		CTRL('['), '[', '1', ';', '5', 'C', 0); /* ctrl right */
1589	kb_add(x_mv_bword,		CTRL('['), '[', '1', ';', '5', 'D', 0); /* ctrl left */
1590}
1591
1592void
1593x_emacs_keys(X_chars *ec)
1594{
1595	x_bind_quiet = 1;
1596	if (ec->erase >= 0) {
1597		kb_add(x_del_back, ec->erase, 0);
1598		kb_add(x_del_bword, CTRL('['), ec->erase, 0);
1599	}
1600	if (ec->kill >= 0)
1601		kb_add(x_del_line, ec->kill, 0);
1602	if (ec->werase >= 0)
1603		kb_add(x_del_bword, ec->werase, 0);
1604	if (ec->intr >= 0)
1605		kb_add(x_abort, ec->intr, 0);
1606	if (ec->quit >= 0)
1607		kb_add(x_noop, ec->quit, 0);
1608	x_bind_quiet = 0;
1609}
1610
1611static int
1612x_set_mark(int c)
1613{
1614	xmp = xcp;
1615	return KSTD;
1616}
1617
1618static int
1619x_kill_region(int c)
1620{
1621	int	rsize;
1622	char	*xr;
1623
1624	if (xmp == NULL) {
1625		x_e_putc(BEL);
1626		return KSTD;
1627	}
1628	if (xmp > xcp) {
1629		rsize = xmp - xcp;
1630		xr = xcp;
1631	} else {
1632		rsize = xcp - xmp;
1633		xr = xmp;
1634	}
1635	x_goto(xr);
1636	x_delete(rsize, true);
1637	xmp = xr;
1638	return KSTD;
1639}
1640
1641static int
1642x_xchg_point_mark(int c)
1643{
1644	char	*tmp;
1645
1646	if (xmp == NULL) {
1647		x_e_putc(BEL);
1648		return KSTD;
1649	}
1650	tmp = xmp;
1651	xmp = xcp;
1652	x_goto( tmp );
1653	return KSTD;
1654}
1655
1656static int
1657x_noop(int c)
1658{
1659	return KSTD;
1660}
1661
1662/*
1663 *	File/command name completion routines
1664 */
1665
1666static int
1667x_comp_comm(int c)
1668{
1669	do_complete(XCF_COMMAND, CT_COMPLETE);
1670	return KSTD;
1671}
1672static int
1673x_list_comm(int c)
1674{
1675	do_complete(XCF_COMMAND, CT_LIST);
1676	return KSTD;
1677}
1678static int
1679x_complete(int c)
1680{
1681	do_complete(XCF_COMMAND_FILE, CT_COMPLETE);
1682	return KSTD;
1683}
1684static int
1685x_enumerate(int c)
1686{
1687	do_complete(XCF_COMMAND_FILE, CT_LIST);
1688	return KSTD;
1689}
1690static int
1691x_comp_file(int c)
1692{
1693	do_complete(XCF_FILE, CT_COMPLETE);
1694	return KSTD;
1695}
1696static int
1697x_list_file(int c)
1698{
1699	do_complete(XCF_FILE, CT_LIST);
1700	return KSTD;
1701}
1702static int
1703x_comp_list(int c)
1704{
1705	do_complete(XCF_COMMAND_FILE, CT_COMPLIST);
1706	return KSTD;
1707}
1708static int
1709x_expand(int c)
1710{
1711	char **words;
1712	int nwords = 0;
1713	int start, end;
1714	int is_command;
1715	int i;
1716
1717	nwords = x_cf_glob(XCF_FILE, xbuf, xep - xbuf, xcp - xbuf,
1718	    &start, &end, &words, &is_command);
1719
1720	if (nwords == 0) {
1721		x_e_putc(BEL);
1722		return KSTD;
1723	}
1724
1725	x_goto(xbuf + start);
1726	x_delete(end - start, false);
1727	for (i = 0; i < nwords;) {
1728		if (x_escape(words[i], strlen(words[i]), x_do_ins) < 0 ||
1729		    (++i < nwords && x_ins(" ") < 0)) {
1730			x_e_putc(BEL);
1731			return KSTD;
1732		}
1733	}
1734	x_adjust();
1735
1736	return KSTD;
1737}
1738
1739/* type == 0 for list, 1 for complete and 2 for complete-list */
1740static void
1741do_complete(int flags,	/* XCF_{COMMAND,FILE,COMMAND_FILE} */
1742    Comp_type type)
1743{
1744	char **words;
1745	int nwords;
1746	int start, end, nlen, olen;
1747	int is_command;
1748	int completed = 0;
1749
1750	nwords = x_cf_glob(flags, xbuf, xep - xbuf, xcp - xbuf,
1751	    &start, &end, &words, &is_command);
1752	/* no match */
1753	if (nwords == 0) {
1754		x_e_putc(BEL);
1755		return;
1756	}
1757
1758	if (type == CT_LIST) {
1759		x_print_expansions(nwords, words, is_command);
1760		x_redraw(0);
1761		x_free_words(nwords, words);
1762		return;
1763	}
1764
1765	olen = end - start;
1766	nlen = x_longest_prefix(nwords, words);
1767	/* complete */
1768	if (nwords == 1 || nlen > olen) {
1769		x_goto(xbuf + start);
1770		x_delete(olen, false);
1771		x_escape(words[0], nlen, x_do_ins);
1772		x_adjust();
1773		completed = 1;
1774	}
1775	/* add space if single non-dir match */
1776	if (nwords == 1 && words[0][nlen - 1] != '/') {
1777		x_ins(" ");
1778		completed = 1;
1779	}
1780
1781	if (type == CT_COMPLIST && !completed) {
1782		x_print_expansions(nwords, words, is_command);
1783		completed = 1;
1784	}
1785
1786	if (completed)
1787		x_redraw(0);
1788
1789	x_free_words(nwords, words);
1790}
1791
1792/* NAME:
1793 *      x_adjust - redraw the line adjusting starting point etc.
1794 *
1795 * DESCRIPTION:
1796 *      This function is called when we have exceeded the bounds
1797 *      of the edit window.  It increments x_adj_done so that
1798 *      functions like x_ins and x_delete know that we have been
1799 *      called and can skip the x_bs() stuff which has already
1800 *      been done by x_redraw.
1801 *
1802 * RETURN VALUE:
1803 *      None
1804 */
1805
1806static void
1807x_adjust(void)
1808{
1809	x_adj_done++;			/* flag the fact that we were called. */
1810	/*
1811	 * we had a problem if the prompt length > xx_cols / 2
1812	 */
1813	if ((xbp = xcp - (x_displen / 2)) < xbuf)
1814		xbp = xbuf;
1815	xlp_valid = false;
1816	x_redraw(xx_cols);
1817	x_flush();
1818}
1819
1820static int unget_char = -1;
1821
1822static void
1823x_e_ungetc(int c)
1824{
1825	unget_char = c;
1826}
1827
1828static int
1829x_e_getc(void)
1830{
1831	int c;
1832
1833	if (unget_char >= 0) {
1834		c = unget_char;
1835		unget_char = -1;
1836	} else if (macro_args) {
1837		c = *macro_args++;
1838		if (!c) {
1839			macro_args = NULL;
1840			c = x_getc();
1841		}
1842	} else
1843		c = x_getc();
1844
1845	return c;
1846}
1847
1848static int
1849x_e_getu8(char *buf, int off)
1850{
1851	int	c, cc, len;
1852
1853	c = x_e_getc();
1854	if (c == -1)
1855		return -1;
1856	buf[off++] = c;
1857
1858	/*
1859	 * In the following, comments refer to violations of
1860	 * the inequality tests at the ends of the lines.
1861	 * See the utf8(7) manual page for details.
1862	 */
1863
1864	if ((c & 0xf8) == 0xf0 && c < 0xf5)  /* beyond Unicode */
1865		len = 4;
1866	else if ((c & 0xf0) == 0xe0)
1867		len = 3;
1868	else if ((c & 0xe0) == 0xc0 && c > 0xc1)  /* use single byte */
1869		len = 2;
1870	else
1871		len = 1;
1872
1873	for (; len > 1; len--) {
1874		cc = x_e_getc();
1875		if (cc == -1)
1876			break;
1877		if (isu8cont(cc) == 0 ||
1878		    (c == 0xe0 && len == 3 && cc < 0xa0) ||  /* use 2 bytes */
1879		    (c == 0xed && len == 3 && cc > 0x9f) ||  /* surrogates  */
1880		    (c == 0xf0 && len == 4 && cc < 0x90) ||  /* use 3 bytes */
1881		    (c == 0xf4 && len == 4 && cc > 0x8f)) {  /* beyond Uni. */
1882			x_e_ungetc(cc);
1883			break;
1884		}
1885		buf[off++] = cc;
1886	}
1887	buf[off] = '\0';
1888
1889	return off;
1890}
1891
1892static void
1893x_e_putc(int c)
1894{
1895	if (c == '\r' || c == '\n')
1896		x_col = 0;
1897	if (x_col < xx_cols) {
1898		x_putc(c);
1899		switch (c) {
1900		case BEL:
1901			break;
1902		case '\r':
1903		case '\n':
1904			break;
1905		case '\b':
1906			x_col--;
1907			break;
1908		default:
1909			if (!isu8cont(c))
1910				x_col++;
1911			break;
1912		}
1913	}
1914	if (x_adj_ok && (x_col < 0 || x_col >= (xx_cols - 2)))
1915		x_adjust();
1916}
1917
1918#ifdef DEBUG
1919static int
1920x_debug_info(int c)
1921{
1922	x_flush();
1923	shellf("\nksh debug:\n");
1924	shellf("\tx_col == %d,\t\tx_cols == %d,\tx_displen == %d\n",
1925	    x_col, xx_cols, x_displen);
1926	shellf("\txcp == 0x%lx,\txep == 0x%lx\n", (long) xcp, (long) xep);
1927	shellf("\txbp == 0x%lx,\txbuf == 0x%lx\n", (long) xbp, (long) xbuf);
1928	shellf("\txlp == 0x%lx\n", (long) xlp);
1929	shellf("\txlp == 0x%lx\n", (long) x_lastcp());
1930	shellf("\n");
1931	x_redraw(-1);
1932	return 0;
1933}
1934#endif
1935
1936static void
1937x_e_puts(const char *s)
1938{
1939	int	adj = x_adj_done;
1940
1941	while (*s && adj == x_adj_done)
1942		x_e_putc(*s++);
1943}
1944
1945/* NAME:
1946 *      x_set_arg - set an arg value for next function
1947 *
1948 * DESCRIPTION:
1949 *      This is a simple implementation of M-[0-9].
1950 *
1951 * RETURN VALUE:
1952 *      KSTD
1953 */
1954
1955static int
1956x_set_arg(int c)
1957{
1958	int n = 0;
1959	int first = 1;
1960
1961	for (; c >= 0 && isdigit(c); c = x_e_getc(), first = 0)
1962		n = n * 10 + (c - '0');
1963	if (c < 0 || first) {
1964		x_e_putc(BEL);
1965		x_arg = 1;
1966		x_arg_defaulted = 1;
1967	} else {
1968		x_e_ungetc(c);
1969		x_arg = n;
1970		x_arg_defaulted = 0;
1971		x_arg_set = 1;
1972	}
1973	return KSTD;
1974}
1975
1976
1977/* Comment or uncomment the current line. */
1978static int
1979x_comment(int c)
1980{
1981	int oldsize = x_size_str(xbuf);
1982	int len = xep - xbuf;
1983	int ret = x_do_comment(xbuf, xend - xbuf, &len);
1984
1985	if (ret < 0)
1986		x_e_putc(BEL);
1987	else {
1988		xep = xbuf + len;
1989		*xep = '\0';
1990		xcp = xbp = xbuf;
1991		x_redraw(oldsize);
1992		if (ret > 0)
1993			return x_newline('\n');
1994	}
1995	return KSTD;
1996}
1997
1998
1999/* NAME:
2000 *      x_prev_histword - recover word from prev command
2001 *
2002 * DESCRIPTION:
2003 *      This function recovers the last word from the previous
2004 *      command and inserts it into the current edit line.  If a
2005 *      numeric arg is supplied then the n'th word from the
2006 *      start of the previous command is used.
2007 *
2008 *      Bound to M-.
2009 *
2010 * RETURN VALUE:
2011 *      KSTD
2012 */
2013
2014static int
2015x_prev_histword(int c)
2016{
2017	char *rcp;
2018	char *cp;
2019
2020	cp = *histptr;
2021	if (!cp)
2022		x_e_putc(BEL);
2023	else if (x_arg_defaulted) {
2024		rcp = &cp[strlen(cp) - 1];
2025		/*
2026		 * ignore white-space after the last word
2027		 */
2028		while (rcp > cp && is_cfs(*rcp))
2029			rcp--;
2030		while (rcp > cp && !is_cfs(*rcp))
2031			rcp--;
2032		if (is_cfs(*rcp))
2033			rcp++;
2034		x_ins(rcp);
2035	} else {
2036		rcp = cp;
2037		/*
2038		 * ignore white-space at start of line
2039		 */
2040		while (*rcp && is_cfs(*rcp))
2041			rcp++;
2042		while (x_arg-- > 1) {
2043			while (*rcp && !is_cfs(*rcp))
2044				rcp++;
2045			while (*rcp && is_cfs(*rcp))
2046				rcp++;
2047		}
2048		cp = rcp;
2049		while (*rcp && !is_cfs(*rcp))
2050			rcp++;
2051		c = *rcp;
2052		*rcp = '\0';
2053		x_ins(cp);
2054		*rcp = c;
2055	}
2056	return KSTD;
2057}
2058
2059/* Uppercase N(1) words */
2060static int
2061x_fold_upper(int c)
2062{
2063	return x_fold_case('U');
2064}
2065
2066/* Lowercase N(1) words */
2067static int
2068x_fold_lower(int c)
2069{
2070	return x_fold_case('L');
2071}
2072
2073/* Lowercase N(1) words */
2074static int
2075x_fold_capitalize(int c)
2076{
2077	return x_fold_case('C');
2078}
2079
2080/* NAME:
2081 *      x_fold_case - convert word to UPPER/lower/Capital case
2082 *
2083 * DESCRIPTION:
2084 *      This function is used to implement M-U,M-u,M-L,M-l,M-C and M-c
2085 *      to UPPER case, lower case or Capitalize words.
2086 *
2087 * RETURN VALUE:
2088 *      None
2089 */
2090
2091static int
2092x_fold_case(int c)
2093{
2094	char *cp = xcp;
2095
2096	if (cp == xep) {
2097		x_e_putc(BEL);
2098		return KSTD;
2099	}
2100	while (x_arg--) {
2101		/*
2102		 * first skip over any white-space
2103		 */
2104		while (cp != xep && is_mfs(*cp))
2105			cp++;
2106		/*
2107		 * do the first char on its own since it may be
2108		 * a different action than for the rest.
2109		 */
2110		if (cp != xep) {
2111			if (c == 'L') {		/* lowercase */
2112				if (isupper((unsigned char)*cp))
2113					*cp = tolower((unsigned char)*cp);
2114			} else {		/* uppercase, capitalize */
2115				if (islower((unsigned char)*cp))
2116					*cp = toupper((unsigned char)*cp);
2117			}
2118			cp++;
2119		}
2120		/*
2121		 * now for the rest of the word
2122		 */
2123		while (cp != xep && !is_mfs(*cp)) {
2124			if (c == 'U') {		/* uppercase */
2125				if (islower((unsigned char)*cp))
2126					*cp = toupper((unsigned char)*cp);
2127			} else {		/* lowercase, capitalize */
2128				if (isupper((unsigned char)*cp))
2129					*cp = tolower((unsigned char)*cp);
2130			}
2131			cp++;
2132		}
2133	}
2134	x_goto(cp);
2135	return KSTD;
2136}
2137
2138/* NAME:
2139 *      x_lastcp - last visible byte
2140 *
2141 * SYNOPSIS:
2142 *      x_lastcp()
2143 *
2144 * DESCRIPTION:
2145 *      This function returns a pointer to that byte in the
2146 *      edit buffer that will be the last displayed on the
2147 *      screen.  The sequence:
2148 *
2149 *      for (cp = x_lastcp(); cp > xcp; cp)
2150 *        x_bs(*--cp);
2151 *
2152 *      Will position the cursor correctly on the screen.
2153 *
2154 * RETURN VALUE:
2155 *      cp or NULL
2156 */
2157
2158static char *
2159x_lastcp(void)
2160{
2161	char *rcp;
2162	int i;
2163
2164	if (!xlp_valid) {
2165		for (i = 0, rcp = xbp; rcp < xep && i < x_displen; rcp++)
2166			i += x_size((unsigned char)*rcp);
2167		xlp = rcp;
2168	}
2169	xlp_valid = true;
2170	return (xlp);
2171}
2172
2173#endif /* EMACS */