loksh-noxz

[fork] a Linux port of OpenBSD's ksh
git clone https://noxz.tech/git/loksh-noxz.git
Log | Files | README

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