xref: /openbsd-src/bin/ksh/emacs.c (revision abe762909b634ba7ba5f0581239afbd78014160a)
1*abe76290Smillert /*	$OpenBSD: emacs.c,v 1.90 2023/06/21 22:22:08 millert Exp $	*/
27cb960a2Sdownsj 
37cb960a2Sdownsj /*
47cb960a2Sdownsj  *  Emacs-like command line editing and history
57cb960a2Sdownsj  *
67cb960a2Sdownsj  *  created by Ron Natalie at BRL
77cb960a2Sdownsj  *  modified by Doug Kingston, Doug Gwyn, and Lou Salkind
87cb960a2Sdownsj  *  adapted to PD ksh by Eric Gisin
9c250457eSdjm  *
10c250457eSdjm  * partial rewrite by Marco Peereboom <marco@openbsd.org>
11c250457eSdjm  * under the same license
127cb960a2Sdownsj  */
137cb960a2Sdownsj 
147cb960a2Sdownsj #include "config.h"
157cb960a2Sdownsj #ifdef EMACS
167cb960a2Sdownsj 
17c250457eSdjm #include <sys/queue.h>
18b608f594Smmcc #include <sys/stat.h>
19b608f594Smmcc 
207cb960a2Sdownsj #include <ctype.h>
214a010e0cStb #include <stdio.h>
224a010e0cStb #include <stdlib.h>
2356018212Smmcc #include <string.h>
24a3c80d85Smillert #ifndef SMALL
25a3c80d85Smillert # include <term.h>
26a3c80d85Smillert # include <curses.h>
27a3c80d85Smillert #endif
28b608f594Smmcc 
29b608f594Smmcc #include "sh.h"
307cb960a2Sdownsj #include "edit.h"
317cb960a2Sdownsj 
327cb960a2Sdownsj static	Area	aedit;
337cb960a2Sdownsj #define	AEDIT	&aedit		/* area for kill ring and macro defns */
347cb960a2Sdownsj 
357cb960a2Sdownsj /* values returned by keyboard functions */
367cb960a2Sdownsj #define	KSTD	0
377cb960a2Sdownsj #define	KEOL	1		/* ^M, ^J */
387cb960a2Sdownsj #define	KINTR	2		/* ^G, ^C */
397cb960a2Sdownsj 
4028297e90Sjca typedef int (*kb_func)(int);
4128297e90Sjca 
427cb960a2Sdownsj struct	x_ftab {
4328297e90Sjca 	kb_func		xf_func;
447cb960a2Sdownsj 	const char	*xf_name;
457cb960a2Sdownsj 	short		xf_flags;
467cb960a2Sdownsj };
477cb960a2Sdownsj 
487cb960a2Sdownsj #define XF_ARG		1	/* command takes number prefix */
497cb960a2Sdownsj #define	XF_NOBIND	2	/* not allowed to bind to function */
507cb960a2Sdownsj #define	XF_PREFIX	4	/* function sets prefix */
517cb960a2Sdownsj 
527cb960a2Sdownsj /* Separator for completion */
537cb960a2Sdownsj #define	is_cfs(c)	(c == ' ' || c == '\t' || c == '"' || c == '\'')
54e569fc7cSderaadt 
55e569fc7cSderaadt /* Separator for motion */
569b414e55Sschwarze #define	is_mfs(c)	(!(isalnum((unsigned char)c) || \
579b414e55Sschwarze 			c == '_' || c == '$' || c & 0x80))
587cb960a2Sdownsj 
597cb960a2Sdownsj /* Arguments for do_complete()
607cb960a2Sdownsj  * 0 = enumerate  M-= complete as much as possible and then list
617cb960a2Sdownsj  * 1 = complete   M-Esc
627cb960a2Sdownsj  * 2 = list       M-?
637cb960a2Sdownsj  */
64b3b39737Sderaadt typedef enum {
65b3b39737Sderaadt 	CT_LIST,	/* list the possible completions */
667cb960a2Sdownsj 	CT_COMPLETE,	/* complete to longest prefix */
677cb960a2Sdownsj 	CT_COMPLIST	/* complete and then list (if non-exact) */
687cb960a2Sdownsj } Comp_type;
697cb960a2Sdownsj 
70c250457eSdjm /* keybindings */
71c250457eSdjm struct kb_entry {
72c250457eSdjm 	TAILQ_ENTRY(kb_entry)	entry;
73c250457eSdjm 	unsigned char		*seq;
74c250457eSdjm 	int			len;
75c250457eSdjm 	struct x_ftab		*ftab;
76c250457eSdjm 	void			*args;
77c250457eSdjm };
78c250457eSdjm TAILQ_HEAD(kb_list, kb_entry);
79c250457eSdjm struct kb_list			kblist = TAILQ_HEAD_INITIALIZER(kblist);
80c250457eSdjm 
817cb960a2Sdownsj /* { from 4.9 edit.h */
827cb960a2Sdownsj /*
837cb960a2Sdownsj  * The following are used for my horizontal scrolling stuff
847cb960a2Sdownsj  */
857cb960a2Sdownsj static char    *xbuf;		/* beg input buffer */
867cb960a2Sdownsj static char    *xend;		/* end input buffer */
877cb960a2Sdownsj static char    *xcp;		/* current position */
887cb960a2Sdownsj static char    *xep;		/* current end */
897cb960a2Sdownsj static char    *xbp;		/* start of visible portion of input buffer */
905cc68a1dSschwarze static char    *xlp;		/* last byte visible on screen */
917cb960a2Sdownsj static int	x_adj_ok;
927cb960a2Sdownsj /*
937cb960a2Sdownsj  * we use x_adj_done so that functions can tell
947cb960a2Sdownsj  * whether x_adjust() has been called while they are active.
957cb960a2Sdownsj  */
967cb960a2Sdownsj static int	x_adj_done;
977cb960a2Sdownsj 
98dcacb757Sdownsj static int	xx_cols;
997cb960a2Sdownsj static int	x_col;
1007cb960a2Sdownsj static int	x_displen;
1017cb960a2Sdownsj static int	x_arg;		/* general purpose arg */
1027cb960a2Sdownsj static int	x_arg_defaulted;/* x_arg not explicitly set; defaulted to 1 */
1037cb960a2Sdownsj 
1047cb960a2Sdownsj static int	xlp_valid;
1057cb960a2Sdownsj /* end from 4.9 edit.h } */
106c250457eSdjm static	int	x_tty;		/* are we on a tty? */
107c250457eSdjm static	int	x_bind_quiet;	/* be quiet when binding keys */
108c250457eSdjm static int	(*x_last_command)(int);
1097cb960a2Sdownsj 
1107cb960a2Sdownsj static	char   **x_histp;	/* history position */
1117cb960a2Sdownsj static	int	x_nextcmd;	/* for newline-and-next */
1127cb960a2Sdownsj static	char	*xmp;		/* mark pointer */
1137cb960a2Sdownsj #define	KILLSIZE	20
1147cb960a2Sdownsj static	char	*killstack[KILLSIZE];
1157cb960a2Sdownsj static	int	killsp, killtp;
116c250457eSdjm static	int	x_literal_set;
117c250457eSdjm static	int	x_arg_set;
11844d96bb9Smpi static	char	*macro_args;
1197cb960a2Sdownsj static	int	prompt_skip;
120638b9e76Sbeck static	int	prompt_redraw;
1217cb960a2Sdownsj 
122c5d5393cSotto static int	x_ins(char *);
123c5d5393cSotto static void	x_delete(int, int);
12469b9f96bSmillert static int	x_bword(void);
12569b9f96bSmillert static int	x_fword(void);
126c5d5393cSotto static void	x_goto(char *);
127c5d5393cSotto static void	x_bs(int);
128c5d5393cSotto static int	x_size_str(char *);
129c5d5393cSotto static int	x_size(int);
130c5d5393cSotto static void	x_zots(char *);
131c5d5393cSotto static void	x_zotc(int);
132c5d5393cSotto static void	x_load_hist(char **);
133c5d5393cSotto static int	x_search(char *, int, int);
134c5d5393cSotto static int	x_match(char *, char *);
135c5d5393cSotto static void	x_redraw(int);
136c5d5393cSotto static void	x_push(int);
13769b9f96bSmillert static void	x_adjust(void);
138c5d5393cSotto static void	x_e_ungetc(int);
13969b9f96bSmillert static int	x_e_getc(void);
1408a9faaaaSanton static int	x_e_getu8(char *, int);
141c5d5393cSotto static void	x_e_putc(int);
142c5d5393cSotto static void	x_e_puts(const char *);
143c5d5393cSotto static int	x_comment(int);
144c5d5393cSotto static int	x_fold_case(int);
14569b9f96bSmillert static char	*x_lastcp(void);
146c5d5393cSotto static void	do_complete(int, Comp_type);
1475cc68a1dSschwarze static int	isu8cont(unsigned char);
1487cb960a2Sdownsj 
149c250457eSdjm /* proto's for keybindings */
150c250457eSdjm static int	x_abort(int);
151c250457eSdjm static int	x_beg_hist(int);
152a3c80d85Smillert static int	x_clear_screen(int);
153c250457eSdjm static int	x_comp_comm(int);
154c250457eSdjm static int	x_comp_file(int);
155c250457eSdjm static int	x_complete(int);
156c250457eSdjm static int	x_del_back(int);
157c250457eSdjm static int	x_del_bword(int);
158c250457eSdjm static int	x_del_char(int);
159c250457eSdjm static int	x_del_fword(int);
160c250457eSdjm static int	x_del_line(int);
161c250457eSdjm static int	x_draw_line(int);
162c250457eSdjm static int	x_end_hist(int);
163c250457eSdjm static int	x_end_of_text(int);
164c250457eSdjm static int	x_enumerate(int);
165c250457eSdjm static int	x_eot_del(int);
166c250457eSdjm static int	x_error(int);
167c250457eSdjm static int	x_goto_hist(int);
168c250457eSdjm static int	x_ins_string(int);
169c250457eSdjm static int	x_insert(int);
170c250457eSdjm static int	x_kill(int);
171c250457eSdjm static int	x_kill_region(int);
172c250457eSdjm static int	x_list_comm(int);
173c250457eSdjm static int	x_list_file(int);
174c250457eSdjm static int	x_literal(int);
175c250457eSdjm static int	x_meta_yank(int);
176c250457eSdjm static int	x_mv_back(int);
177c250457eSdjm static int	x_mv_begin(int);
178c250457eSdjm static int	x_mv_bword(int);
179c250457eSdjm static int	x_mv_end(int);
180c250457eSdjm static int	x_mv_forw(int);
181c250457eSdjm static int	x_mv_fword(int);
182c250457eSdjm static int	x_newline(int);
183c250457eSdjm static int	x_next_com(int);
184c250457eSdjm static int	x_nl_next_com(int);
185c250457eSdjm static int	x_noop(int);
186c250457eSdjm static int	x_prev_com(int);
187c250457eSdjm static int	x_prev_histword(int);
188c250457eSdjm static int	x_search_char_forw(int);
189c250457eSdjm static int	x_search_char_back(int);
190c250457eSdjm static int	x_search_hist(int);
191c250457eSdjm static int	x_set_mark(int);
192c250457eSdjm static int	x_transpose(int);
193c250457eSdjm static int	x_xchg_point_mark(int);
194c250457eSdjm static int	x_yank(int);
195c250457eSdjm static int	x_comp_list(int);
196c250457eSdjm static int	x_expand(int);
197c250457eSdjm static int	x_fold_capitalize(int);
198c250457eSdjm static int	x_fold_lower(int);
199c250457eSdjm static int	x_fold_upper(int);
200c250457eSdjm static int	x_set_arg(int);
201c250457eSdjm static int	x_comment(int);
202c250457eSdjm #ifdef DEBUG
203c250457eSdjm static int	x_debug_info(int);
204c250457eSdjm #endif
2057cb960a2Sdownsj 
2067cb960a2Sdownsj static const struct x_ftab x_ftab[] = {
2077cb960a2Sdownsj 	{ x_abort,		"abort",			0 },
2087cb960a2Sdownsj 	{ x_beg_hist,		"beginning-of-history",		0 },
209a3c80d85Smillert 	{ x_clear_screen,	"clear-screen",			0 },
2107cb960a2Sdownsj 	{ x_comp_comm,		"complete-command",		0 },
2117cb960a2Sdownsj 	{ x_comp_file,		"complete-file",		0 },
2127cb960a2Sdownsj 	{ x_complete,		"complete",			0 },
2137cb960a2Sdownsj 	{ x_del_back,		"delete-char-backward",		XF_ARG },
2147cb960a2Sdownsj 	{ x_del_bword,		"delete-word-backward",		XF_ARG },
2157cb960a2Sdownsj 	{ x_del_char,		"delete-char-forward",		XF_ARG },
2167cb960a2Sdownsj 	{ x_del_fword,		"delete-word-forward",		XF_ARG },
2177cb960a2Sdownsj 	{ x_del_line,		"kill-line",			0 },
2187cb960a2Sdownsj 	{ x_draw_line,		"redraw",			0 },
2197cb960a2Sdownsj 	{ x_end_hist,		"end-of-history",		0 },
2207cb960a2Sdownsj 	{ x_end_of_text,	"eot",				0 },
2217cb960a2Sdownsj 	{ x_enumerate,		"list",				0 },
2227cb960a2Sdownsj 	{ x_eot_del,		"eot-or-delete",		XF_ARG },
2237cb960a2Sdownsj 	{ x_error,		"error",			0 },
2247cb960a2Sdownsj 	{ x_goto_hist,		"goto-history",			XF_ARG },
2257cb960a2Sdownsj 	{ x_ins_string,		"macro-string",			XF_NOBIND },
2267cb960a2Sdownsj 	{ x_insert,		"auto-insert",			XF_ARG },
2277cb960a2Sdownsj 	{ x_kill,		"kill-to-eol",			XF_ARG },
2287cb960a2Sdownsj 	{ x_kill_region,	"kill-region",			0 },
2297cb960a2Sdownsj 	{ x_list_comm,		"list-command",			0 },
2307cb960a2Sdownsj 	{ x_list_file,		"list-file",			0 },
2317cb960a2Sdownsj 	{ x_literal,		"quote",			0 },
2327cb960a2Sdownsj 	{ x_meta_yank,		"yank-pop",			0 },
2337cb960a2Sdownsj 	{ x_mv_back,		"backward-char",		XF_ARG },
2347cb960a2Sdownsj 	{ x_mv_begin,		"beginning-of-line",		0 },
2357cb960a2Sdownsj 	{ x_mv_bword,		"backward-word",		XF_ARG },
2367cb960a2Sdownsj 	{ x_mv_end,		"end-of-line",			0 },
2377cb960a2Sdownsj 	{ x_mv_forw,		"forward-char",			XF_ARG },
2387cb960a2Sdownsj 	{ x_mv_fword,		"forward-word",			XF_ARG },
2397cb960a2Sdownsj 	{ x_newline,		"newline",			0 },
2407cb960a2Sdownsj 	{ x_next_com,		"down-history",			XF_ARG },
2417cb960a2Sdownsj 	{ x_nl_next_com,	"newline-and-next",		0 },
2427cb960a2Sdownsj 	{ x_noop,		"no-op",			0 },
2437cb960a2Sdownsj 	{ x_prev_com,		"up-history",			XF_ARG },
2447cb960a2Sdownsj 	{ x_prev_histword,	"prev-hist-word",		XF_ARG },
2457cb960a2Sdownsj 	{ x_search_char_forw,	"search-character-forward",	XF_ARG },
2467cb960a2Sdownsj 	{ x_search_char_back,	"search-character-backward",	XF_ARG },
2477cb960a2Sdownsj 	{ x_search_hist,	"search-history",		0 },
2487cb960a2Sdownsj 	{ x_set_mark,		"set-mark-command",		0 },
2497cb960a2Sdownsj 	{ x_transpose,		"transpose-chars",		0 },
2507cb960a2Sdownsj 	{ x_xchg_point_mark,	"exchange-point-and-mark",	0 },
2517cb960a2Sdownsj 	{ x_yank,		"yank",				0 },
2527cb960a2Sdownsj 	{ x_comp_list,		"complete-list",		0 },
2537cb960a2Sdownsj 	{ x_expand,		"expand-file",			0 },
254060cee32Sjmc 	{ x_fold_capitalize,	"capitalize-word",		XF_ARG },
2557cb960a2Sdownsj 	{ x_fold_lower,		"downcase-word",		XF_ARG },
2567cb960a2Sdownsj 	{ x_fold_upper,		"upcase-word",			XF_ARG },
2577cb960a2Sdownsj 	{ x_set_arg,		"set-arg",			XF_NOBIND },
258dcacb757Sdownsj 	{ x_comment,		"comment",			0 },
2597cb960a2Sdownsj 	{ 0, 0, 0 },
2607cb960a2Sdownsj #ifdef DEBUG
2617cb960a2Sdownsj 	{ x_debug_info,		"debug-info",			0 },
2627cb960a2Sdownsj #else
2637cb960a2Sdownsj 	{ 0, 0, 0 },
2647cb960a2Sdownsj #endif
2657cb960a2Sdownsj 	{ 0, 0, 0 },
2667cb960a2Sdownsj };
2677cb960a2Sdownsj 
2687cb960a2Sdownsj int
isu8cont(unsigned char c)2695cc68a1dSschwarze isu8cont(unsigned char c)
2705cc68a1dSschwarze {
2715cc68a1dSschwarze 	return (c & (0x80 | 0x40)) == 0x80;
2725cc68a1dSschwarze }
2735cc68a1dSschwarze 
2745cc68a1dSschwarze int
x_emacs(char * buf,size_t len)275c5d5393cSotto x_emacs(char *buf, size_t len)
2767cb960a2Sdownsj {
277c250457eSdjm 	struct kb_entry		*k, *kmatch = NULL;
278c250457eSdjm 	char			line[LINE + 1];
2798a9faaaaSanton 	int			at = 0, ntries = 0, submatch, ret;
2807cb960a2Sdownsj 	const char		*p;
2817cb960a2Sdownsj 
2827cb960a2Sdownsj 	xbp = xbuf = buf; xend = buf + len;
2837cb960a2Sdownsj 	xlp = xcp = xep = buf;
2847cb960a2Sdownsj 	*xcp = 0;
2850e7d3a01Smillert 	xlp_valid = true;
2867cb960a2Sdownsj 	xmp = NULL;
2877cb960a2Sdownsj 	x_histp = histptr + 1;
2887cb960a2Sdownsj 
289dcacb757Sdownsj 	xx_cols = x_cols;
2907cb960a2Sdownsj 	x_col = promptlen(prompt, &p);
2917cb960a2Sdownsj 	prompt_skip = p - prompt;
2927cb960a2Sdownsj 	x_adj_ok = 1;
293638b9e76Sbeck 	prompt_redraw = 1;
294638b9e76Sbeck 	if (x_col > xx_cols)
295638b9e76Sbeck 		x_col = x_col - (x_col / xx_cols) * xx_cols;
296dcacb757Sdownsj 	x_displen = xx_cols - 2 - x_col;
2977cb960a2Sdownsj 	x_adj_done = 0;
2987cb960a2Sdownsj 
2997cb960a2Sdownsj 	pprompt(prompt, 0);
300638b9e76Sbeck 	if (x_displen < 1) {
301638b9e76Sbeck 		x_col = 0;
302638b9e76Sbeck 		x_displen = xx_cols - 2;
303638b9e76Sbeck 		x_e_putc('\n');
304638b9e76Sbeck 		prompt_redraw = 0;
305638b9e76Sbeck 	}
3067cb960a2Sdownsj 
3077cb960a2Sdownsj 	if (x_nextcmd >= 0) {
3087cb960a2Sdownsj 		int off = source->line - x_nextcmd;
3097cb960a2Sdownsj 		if (histptr - history >= off)
3107cb960a2Sdownsj 			x_load_hist(histptr - off);
3117cb960a2Sdownsj 		x_nextcmd = -1;
3127cb960a2Sdownsj 	}
3137cb960a2Sdownsj 
314c250457eSdjm 	x_literal_set = 0;
315c250457eSdjm 	x_arg = -1;
316c250457eSdjm 	x_last_command = NULL;
3177cb960a2Sdownsj 	while (1) {
3187cb960a2Sdownsj 		x_flush();
3198a9faaaaSanton 		if ((at = x_e_getu8(line, at)) < 0)
3207cb960a2Sdownsj 			return 0;
3218a9faaaaSanton 		ntries++;
3226cef2a06Smillert 
323c250457eSdjm 		if (x_arg == -1) {
3247cb960a2Sdownsj 			x_arg = 1;
3257cb960a2Sdownsj 			x_arg_defaulted = 1;
3267cb960a2Sdownsj 		}
327c250457eSdjm 
328c250457eSdjm 		if (x_literal_set) {
329c250457eSdjm 			/* literal, so insert it */
330c250457eSdjm 			x_literal_set = 0;
331c250457eSdjm 			submatch = 0;
332c250457eSdjm 		} else {
333c250457eSdjm 			submatch = 0;
334c250457eSdjm 			kmatch = NULL;
335c250457eSdjm 			TAILQ_FOREACH(k, &kblist, entry) {
336c250457eSdjm 				if (at > k->len)
337c250457eSdjm 					continue;
338c250457eSdjm 
33989253508Stedu 				if (memcmp(k->seq, line, at) == 0) {
340c250457eSdjm 					/* sub match */
341c250457eSdjm 					submatch++;
342c250457eSdjm 					if (k->len == at)
343c250457eSdjm 						kmatch = k;
344c250457eSdjm 				}
345c250457eSdjm 
346c250457eSdjm 				/* see if we can abort search early */
347c250457eSdjm 				if (submatch > 1)
348c250457eSdjm 					break;
349c250457eSdjm 			}
350c250457eSdjm 		}
351c250457eSdjm 
352c250457eSdjm 		if (submatch == 1 && kmatch) {
353c250457eSdjm 			if (kmatch->ftab->xf_func == x_ins_string &&
35444d96bb9Smpi 			    kmatch->args && !macro_args) {
35544d96bb9Smpi 				/* treat macro string as input */
35644d96bb9Smpi 				macro_args = kmatch->args;
35744d96bb9Smpi 				ret = KSTD;
35844d96bb9Smpi 			} else
3598a9faaaaSanton 				ret = kmatch->ftab->xf_func(line[at - 1]);
360c250457eSdjm 		} else {
361c250457eSdjm 			if (submatch)
362c250457eSdjm 				continue;
3638a9faaaaSanton 			if (ntries > 1) {
3648a9faaaaSanton 				ret = x_error(0); /* unmatched meta sequence */
3658a9faaaaSanton 			} else if (at > 1) {
3668a9faaaaSanton 				x_ins(line);
3678a9faaaaSanton 				ret = KSTD;
3688a9faaaaSanton 			} else {
3698a9faaaaSanton 				ret = x_insert(line[0]);
3708a9faaaaSanton 			}
371c250457eSdjm 		}
372c250457eSdjm 
373c250457eSdjm 		switch (ret) {
3747cb960a2Sdownsj 		case KSTD:
375c250457eSdjm 			if (kmatch)
376c250457eSdjm 				x_last_command = kmatch->ftab->xf_func;
377c250457eSdjm 			else
378c250457eSdjm 				x_last_command = NULL;
3797cb960a2Sdownsj 			break;
3807cb960a2Sdownsj 		case KEOL:
381c250457eSdjm 			ret = xep - xbuf;
382c250457eSdjm 			return (ret);
383c250457eSdjm 			break;
384c250457eSdjm 		case KINTR:
3857cb960a2Sdownsj 			trapsig(SIGINT);
3860e7d3a01Smillert 			x_mode(false);
3877cb960a2Sdownsj 			unwind(LSHELL);
388c250457eSdjm 			x_arg = -1;
389c250457eSdjm 			break;
390c250457eSdjm 		default:
391c250457eSdjm 			bi_errorf("invalid return code"); /* can't happen */
3927cb960a2Sdownsj 		}
393c250457eSdjm 
394c250457eSdjm 		/* reset meta sequence */
3958a9faaaaSanton 		at = ntries = 0;
396c250457eSdjm 		if (x_arg_set)
397c250457eSdjm 			x_arg_set = 0; /* reset args next time around */
398c250457eSdjm 		else
399c250457eSdjm 			x_arg = -1;
4007cb960a2Sdownsj 	}
4017cb960a2Sdownsj }
4027cb960a2Sdownsj 
4037cb960a2Sdownsj static int
x_insert(int c)404c5d5393cSotto x_insert(int c)
4057cb960a2Sdownsj {
4067cb960a2Sdownsj 	char	str[2];
4077cb960a2Sdownsj 
4087cb960a2Sdownsj 	/*
4097cb960a2Sdownsj 	 *  Should allow tab and control chars.
4107cb960a2Sdownsj 	 */
4117cb960a2Sdownsj 	if (c == 0) {
4127cb960a2Sdownsj 		x_e_putc(BEL);
4137cb960a2Sdownsj 		return KSTD;
4147cb960a2Sdownsj 	}
4157cb960a2Sdownsj 	str[0] = c;
4167cb960a2Sdownsj 	str[1] = '\0';
4177cb960a2Sdownsj 	while (x_arg--)
4187cb960a2Sdownsj 		x_ins(str);
4197cb960a2Sdownsj 	return KSTD;
4207cb960a2Sdownsj }
4217cb960a2Sdownsj 
4227cb960a2Sdownsj static int
x_ins_string(int c)423c5d5393cSotto x_ins_string(int c)
4247cb960a2Sdownsj {
42544d96bb9Smpi 	return x_insert(c);
4267cb960a2Sdownsj }
4277cb960a2Sdownsj 
4287cb960a2Sdownsj static int
x_do_ins(const char * cp,size_t len)429f369c404Smmcc x_do_ins(const char *cp, size_t len)
4307cb960a2Sdownsj {
4317cb960a2Sdownsj 	if (xep+len >= xend) {
4327cb960a2Sdownsj 		x_e_putc(BEL);
4337cb960a2Sdownsj 		return -1;
4347cb960a2Sdownsj 	}
4357cb960a2Sdownsj 
4367cb960a2Sdownsj 	memmove(xcp+len, xcp, xep - xcp + 1);
4377cb960a2Sdownsj 	memmove(xcp, cp, len);
4387cb960a2Sdownsj 	xcp += len;
4397cb960a2Sdownsj 	xep += len;
4407cb960a2Sdownsj 	return 0;
4417cb960a2Sdownsj }
4427cb960a2Sdownsj 
4437cb960a2Sdownsj static int
x_ins(char * s)444c5d5393cSotto x_ins(char *s)
4457cb960a2Sdownsj {
4467cb960a2Sdownsj 	char	*cp = xcp;
4477894b443Smillert 	int	adj = x_adj_done;
4487cb960a2Sdownsj 
4497cb960a2Sdownsj 	if (x_do_ins(s, strlen(s)) < 0)
4507cb960a2Sdownsj 		return -1;
4517cb960a2Sdownsj 	/*
4527cb960a2Sdownsj 	 * x_zots() may result in a call to x_adjust()
4537cb960a2Sdownsj 	 * we want xcp to reflect the new position.
4547cb960a2Sdownsj 	 */
4550e7d3a01Smillert 	xlp_valid = false;
4567cb960a2Sdownsj 	x_lastcp();
4577cb960a2Sdownsj 	x_adj_ok = (xcp >= xlp);
4587cb960a2Sdownsj 	x_zots(cp);
4597a8124d8Sderaadt 	if (adj == x_adj_done) {	/* has x_adjust() been called? */
4607cb960a2Sdownsj 		/* no */
4617cb960a2Sdownsj 		for (cp = xlp; cp > xcp; )
4627cb960a2Sdownsj 			x_bs(*--cp);
4637cb960a2Sdownsj 	}
4647cb960a2Sdownsj 
4657cb960a2Sdownsj 	x_adj_ok = 1;
4667cb960a2Sdownsj 	return 0;
4677cb960a2Sdownsj }
4687cb960a2Sdownsj 
4697cb960a2Sdownsj static int
x_del_back(int c)470c5d5393cSotto x_del_back(int c)
4717cb960a2Sdownsj {
4727cb960a2Sdownsj 	int col = xcp - xbuf;
4737cb960a2Sdownsj 
4747cb960a2Sdownsj 	if (col == 0) {
4757cb960a2Sdownsj 		x_e_putc(BEL);
4767cb960a2Sdownsj 		return KSTD;
4777cb960a2Sdownsj 	}
4787cb960a2Sdownsj 	if (x_arg > col)
4797cb960a2Sdownsj 		x_arg = col;
4805cc68a1dSschwarze 	while (x_arg < col && isu8cont(xcp[-x_arg]))
4815cc68a1dSschwarze 		x_arg++;
4827cb960a2Sdownsj 	x_goto(xcp - x_arg);
4830e7d3a01Smillert 	x_delete(x_arg, false);
4847cb960a2Sdownsj 	return KSTD;
4857cb960a2Sdownsj }
4867cb960a2Sdownsj 
4877cb960a2Sdownsj static int
x_del_char(int c)488c5d5393cSotto x_del_char(int c)
4897cb960a2Sdownsj {
4907cb960a2Sdownsj 	int nleft = xep - xcp;
4917cb960a2Sdownsj 
4927cb960a2Sdownsj 	if (!nleft) {
4937cb960a2Sdownsj 		x_e_putc(BEL);
4947cb960a2Sdownsj 		return KSTD;
4957cb960a2Sdownsj 	}
4967cb960a2Sdownsj 	if (x_arg > nleft)
4977cb960a2Sdownsj 		x_arg = nleft;
4985cc68a1dSschwarze 	while (x_arg < nleft && isu8cont(xcp[x_arg]))
4995cc68a1dSschwarze 		x_arg++;
5000e7d3a01Smillert 	x_delete(x_arg, false);
5017cb960a2Sdownsj 	return KSTD;
5027cb960a2Sdownsj }
5037cb960a2Sdownsj 
5045cc68a1dSschwarze /* Delete nc bytes to the right of the cursor (including cursor position) */
5057cb960a2Sdownsj static void
x_delete(int nc,int push)506c5d5393cSotto x_delete(int nc, int push)
5077cb960a2Sdownsj {
5087cb960a2Sdownsj 	int	i,j;
5097cb960a2Sdownsj 	char	*cp;
5107cb960a2Sdownsj 
5117cb960a2Sdownsj 	if (nc == 0)
5127cb960a2Sdownsj 		return;
5137cb960a2Sdownsj 	if (xmp != NULL && xmp > xcp) {
5147cb960a2Sdownsj 		if (xcp + nc > xmp)
5157cb960a2Sdownsj 			xmp = xcp;
5167cb960a2Sdownsj 		else
5177cb960a2Sdownsj 			xmp -= nc;
5187cb960a2Sdownsj 	}
5197cb960a2Sdownsj 
5207cb960a2Sdownsj 	/*
5217cb960a2Sdownsj 	 * This lets us yank a word we have deleted.
5227cb960a2Sdownsj 	 */
52392303ab9Sfgsch 	if (push)
5247cb960a2Sdownsj 		x_push(nc);
5257cb960a2Sdownsj 
5267cb960a2Sdownsj 	xep -= nc;
5277cb960a2Sdownsj 	cp = xcp;
5287cb960a2Sdownsj 	j = 0;
5297cb960a2Sdownsj 	i = nc;
5307cb960a2Sdownsj 	while (i--) {
531e569fc7cSderaadt 		j += x_size((unsigned char)*cp++);
5327cb960a2Sdownsj 	}
5337cb960a2Sdownsj 	memmove(xcp, xcp+nc, xep - xcp + 1);	/* Copies the null */
5347cb960a2Sdownsj 	x_adj_ok = 0;			/* don't redraw */
5353e173141Sschwarze 	xlp_valid = false;
5367cb960a2Sdownsj 	x_zots(xcp);
5377cb960a2Sdownsj 	/*
5387cb960a2Sdownsj 	 * if we are already filling the line,
5397cb960a2Sdownsj 	 * there is no need to ' ','\b'.
5407cb960a2Sdownsj 	 * But if we must, make sure we do the minimum.
5417cb960a2Sdownsj 	 */
5427a8124d8Sderaadt 	if ((i = xx_cols - 2 - x_col) > 0) {
5437cb960a2Sdownsj 		j = (j < i) ? j : i;
5447cb960a2Sdownsj 		i = j;
5457cb960a2Sdownsj 		while (i--)
5467cb960a2Sdownsj 			x_e_putc(' ');
5477cb960a2Sdownsj 		i = j;
5487cb960a2Sdownsj 		while (i--)
5497cb960a2Sdownsj 			x_e_putc('\b');
5507cb960a2Sdownsj 	}
5517cb960a2Sdownsj 	/*x_goto(xcp);*/
5527cb960a2Sdownsj 	x_adj_ok = 1;
5530e7d3a01Smillert 	xlp_valid = false;
5547cb960a2Sdownsj 	for (cp = x_lastcp(); cp > xcp; )
5557cb960a2Sdownsj 		x_bs(*--cp);
5567cb960a2Sdownsj 
5577cb960a2Sdownsj 	return;
5587cb960a2Sdownsj }
5597cb960a2Sdownsj 
5607cb960a2Sdownsj static int
x_del_bword(int c)561c5d5393cSotto x_del_bword(int c)
5627cb960a2Sdownsj {
5630e7d3a01Smillert 	x_delete(x_bword(), true);
5647cb960a2Sdownsj 	return KSTD;
5657cb960a2Sdownsj }
5667cb960a2Sdownsj 
5677cb960a2Sdownsj static int
x_mv_bword(int c)568c5d5393cSotto x_mv_bword(int c)
5697cb960a2Sdownsj {
5707cb960a2Sdownsj 	(void)x_bword();
5717cb960a2Sdownsj 	return KSTD;
5727cb960a2Sdownsj }
5737cb960a2Sdownsj 
5747cb960a2Sdownsj static int
x_mv_fword(int c)575c5d5393cSotto x_mv_fword(int c)
5767cb960a2Sdownsj {
5777cb960a2Sdownsj 	x_goto(xcp + x_fword());
5787cb960a2Sdownsj 	return KSTD;
5797cb960a2Sdownsj }
5807cb960a2Sdownsj 
5817cb960a2Sdownsj static int
x_del_fword(int c)582c5d5393cSotto x_del_fword(int c)
5837cb960a2Sdownsj {
5840e7d3a01Smillert 	x_delete(x_fword(), true);
5857cb960a2Sdownsj 	return KSTD;
5867cb960a2Sdownsj }
5877cb960a2Sdownsj 
5887cb960a2Sdownsj static int
x_bword(void)589c5d5393cSotto x_bword(void)
5907cb960a2Sdownsj {
5917cb960a2Sdownsj 	int	nc = 0;
5927894b443Smillert 	char	*cp = xcp;
5937cb960a2Sdownsj 
5947cb960a2Sdownsj 	if (cp == xbuf) {
5957cb960a2Sdownsj 		x_e_putc(BEL);
5967cb960a2Sdownsj 		return 0;
5977cb960a2Sdownsj 	}
5987a8124d8Sderaadt 	while (x_arg--) {
5997a8124d8Sderaadt 		while (cp != xbuf && is_mfs(cp[-1])) {
6007cb960a2Sdownsj 			cp--;
6017cb960a2Sdownsj 			nc++;
6027cb960a2Sdownsj 		}
6037a8124d8Sderaadt 		while (cp != xbuf && !is_mfs(cp[-1])) {
6047cb960a2Sdownsj 			cp--;
6057cb960a2Sdownsj 			nc++;
6067cb960a2Sdownsj 		}
6077cb960a2Sdownsj 	}
6087cb960a2Sdownsj 	x_goto(cp);
6097cb960a2Sdownsj 	return nc;
6107cb960a2Sdownsj }
6117cb960a2Sdownsj 
6127cb960a2Sdownsj static int
x_fword(void)613c5d5393cSotto x_fword(void)
6147cb960a2Sdownsj {
6157cb960a2Sdownsj 	int	nc = 0;
6167894b443Smillert 	char	*cp = xcp;
6177cb960a2Sdownsj 
6187cb960a2Sdownsj 	if (cp == xep) {
6197cb960a2Sdownsj 		x_e_putc(BEL);
6207cb960a2Sdownsj 		return 0;
6217cb960a2Sdownsj 	}
6227a8124d8Sderaadt 	while (x_arg--) {
6237a8124d8Sderaadt 		while (cp != xep && is_mfs(*cp)) {
6247cb960a2Sdownsj 			cp++;
6257cb960a2Sdownsj 			nc++;
6267cb960a2Sdownsj 		}
6277a8124d8Sderaadt 		while (cp != xep && !is_mfs(*cp)) {
6287cb960a2Sdownsj 			cp++;
6297cb960a2Sdownsj 			nc++;
6307cb960a2Sdownsj 		}
6317cb960a2Sdownsj 	}
6327cb960a2Sdownsj 	return nc;
6337cb960a2Sdownsj }
6347cb960a2Sdownsj 
6357cb960a2Sdownsj static void
x_goto(char * cp)636c5d5393cSotto x_goto(char *cp)
6377cb960a2Sdownsj {
6387a8124d8Sderaadt 	if (cp < xbp || cp >= (xbp + x_displen)) {
6397cb960a2Sdownsj 		/* we are heading off screen */
6407cb960a2Sdownsj 		xcp = cp;
6417cb960a2Sdownsj 		x_adjust();
6427a8124d8Sderaadt 	} else if (cp < xcp) {		/* move back */
6437cb960a2Sdownsj 		while (cp < xcp)
644e569fc7cSderaadt 			x_bs((unsigned char)*--xcp);
6457a8124d8Sderaadt 	} else if (cp > xcp) {		/* move forward */
6467cb960a2Sdownsj 		while (cp > xcp)
647e569fc7cSderaadt 			x_zotc((unsigned char)*xcp++);
6487cb960a2Sdownsj 	}
6497cb960a2Sdownsj }
6507cb960a2Sdownsj 
6517cb960a2Sdownsj static void
x_bs(int c)652c5d5393cSotto x_bs(int c)
6537cb960a2Sdownsj {
6547894b443Smillert 	int i;
6557a8124d8Sderaadt 
6567cb960a2Sdownsj 	i = x_size(c);
6577cb960a2Sdownsj 	while (i--)
6587cb960a2Sdownsj 		x_e_putc('\b');
6597cb960a2Sdownsj }
6607cb960a2Sdownsj 
6617cb960a2Sdownsj static int
x_size_str(char * cp)662c5d5393cSotto x_size_str(char *cp)
6637cb960a2Sdownsj {
6647894b443Smillert 	int size = 0;
6657cb960a2Sdownsj 	while (*cp)
6667cb960a2Sdownsj 		size += x_size(*cp++);
6677cb960a2Sdownsj 	return size;
6687cb960a2Sdownsj }
6697cb960a2Sdownsj 
6707cb960a2Sdownsj static int
x_size(int c)671c5d5393cSotto x_size(int c)
6727cb960a2Sdownsj {
6737cb960a2Sdownsj 	if (c=='\t')
6747cb960a2Sdownsj 		return 4;	/* Kludge, tabs are always four spaces. */
6752dee7088Smillert 	if (iscntrl(c))		/* control char */
6767cb960a2Sdownsj 		return 2;
6775cc68a1dSschwarze 	if (isu8cont(c))
6785cc68a1dSschwarze 		return 0;
6797cb960a2Sdownsj 	return 1;
6807cb960a2Sdownsj }
6817cb960a2Sdownsj 
6827cb960a2Sdownsj static void
x_zots(char * str)683c5d5393cSotto x_zots(char *str)
6847cb960a2Sdownsj {
6857894b443Smillert 	int	adj = x_adj_done;
6867cb960a2Sdownsj 
6879b414e55Sschwarze 	if (str > xbuf && isu8cont(*str)) {
6889b414e55Sschwarze 		while (str > xbuf && isu8cont(*str))
6899b414e55Sschwarze 			str--;
6909b414e55Sschwarze 		x_e_putc('\b');
6919b414e55Sschwarze 	}
6927cb960a2Sdownsj 	x_lastcp();
6937cb960a2Sdownsj 	while (*str && str < xlp && adj == x_adj_done)
6947cb960a2Sdownsj 		x_zotc(*str++);
6957cb960a2Sdownsj }
6967cb960a2Sdownsj 
6977cb960a2Sdownsj static void
x_zotc(int c)698c5d5393cSotto x_zotc(int c)
6997cb960a2Sdownsj {
7007cb960a2Sdownsj 	if (c == '\t') {
7017cb960a2Sdownsj 		/*  Kludge, tabs are always four spaces.  */
7027cb960a2Sdownsj 		x_e_puts("    ");
7032dee7088Smillert 	} else if (iscntrl(c)) {
7047cb960a2Sdownsj 		x_e_putc('^');
7057cb960a2Sdownsj 		x_e_putc(UNCTRL(c));
7067cb960a2Sdownsj 	} else
7077cb960a2Sdownsj 		x_e_putc(c);
7087cb960a2Sdownsj }
7097cb960a2Sdownsj 
7107cb960a2Sdownsj static int
x_mv_back(int c)711c5d5393cSotto x_mv_back(int c)
7127cb960a2Sdownsj {
7137cb960a2Sdownsj 	int col = xcp - xbuf;
7147cb960a2Sdownsj 
7157cb960a2Sdownsj 	if (col == 0) {
7167cb960a2Sdownsj 		x_e_putc(BEL);
7177cb960a2Sdownsj 		return KSTD;
7187cb960a2Sdownsj 	}
7197cb960a2Sdownsj 	if (x_arg > col)
7207cb960a2Sdownsj 		x_arg = col;
7215cc68a1dSschwarze 	while (x_arg < col && isu8cont(xcp[-x_arg]))
7225cc68a1dSschwarze 		x_arg++;
7237cb960a2Sdownsj 	x_goto(xcp - x_arg);
7247cb960a2Sdownsj 	return KSTD;
7257cb960a2Sdownsj }
7267cb960a2Sdownsj 
7277cb960a2Sdownsj static int
x_mv_forw(int c)728c5d5393cSotto x_mv_forw(int c)
7297cb960a2Sdownsj {
7307cb960a2Sdownsj 	int nleft = xep - xcp;
7317cb960a2Sdownsj 
7327cb960a2Sdownsj 	if (!nleft) {
7337cb960a2Sdownsj 		x_e_putc(BEL);
7347cb960a2Sdownsj 		return KSTD;
7357cb960a2Sdownsj 	}
7367cb960a2Sdownsj 	if (x_arg > nleft)
7377cb960a2Sdownsj 		x_arg = nleft;
7389b414e55Sschwarze 	while (x_arg < nleft && isu8cont(xcp[x_arg]))
7395cc68a1dSschwarze 		x_arg++;
7407cb960a2Sdownsj 	x_goto(xcp + x_arg);
7417cb960a2Sdownsj 	return KSTD;
7427cb960a2Sdownsj }
7437cb960a2Sdownsj 
7447cb960a2Sdownsj static int
x_search_char_forw(int c)745c5d5393cSotto x_search_char_forw(int c)
7467cb960a2Sdownsj {
7477cb960a2Sdownsj 	char *cp = xcp;
7487cb960a2Sdownsj 
7497cb960a2Sdownsj 	*xep = '\0';
7507cb960a2Sdownsj 	c = x_e_getc();
7517cb960a2Sdownsj 	while (x_arg--) {
7527a8124d8Sderaadt 		if (c < 0 ||
7537a8124d8Sderaadt 		    ((cp = (cp == xep) ? NULL : strchr(cp + 1, c)) == NULL &&
7547a8124d8Sderaadt 		    (cp = strchr(xbuf, c)) == NULL)) {
7557cb960a2Sdownsj 			x_e_putc(BEL);
7567cb960a2Sdownsj 			return KSTD;
7577cb960a2Sdownsj 		}
7587cb960a2Sdownsj 	}
7597cb960a2Sdownsj 	x_goto(cp);
7607cb960a2Sdownsj 	return KSTD;
7617cb960a2Sdownsj }
7627cb960a2Sdownsj 
7637cb960a2Sdownsj static int
x_search_char_back(int c)764c5d5393cSotto x_search_char_back(int c)
7657cb960a2Sdownsj {
7667cb960a2Sdownsj 	char *cp = xcp, *p;
7677cb960a2Sdownsj 
7687cb960a2Sdownsj 	c = x_e_getc();
7697cb960a2Sdownsj 	for (; x_arg--; cp = p)
7707cb960a2Sdownsj 		for (p = cp; ; ) {
7717cb960a2Sdownsj 			if (p-- == xbuf)
7727cb960a2Sdownsj 				p = xep;
7737cb960a2Sdownsj 			if (c < 0 || p == cp) {
7747cb960a2Sdownsj 				x_e_putc(BEL);
7757cb960a2Sdownsj 				return KSTD;
7767cb960a2Sdownsj 			}
7777cb960a2Sdownsj 			if (*p == c)
7787cb960a2Sdownsj 				break;
7797cb960a2Sdownsj 		}
7807cb960a2Sdownsj 	x_goto(cp);
7817cb960a2Sdownsj 	return KSTD;
7827cb960a2Sdownsj }
7837cb960a2Sdownsj 
7847cb960a2Sdownsj static int
x_newline(int c)785c5d5393cSotto x_newline(int c)
7867cb960a2Sdownsj {
7877cb960a2Sdownsj 	x_e_putc('\r');
7887cb960a2Sdownsj 	x_e_putc('\n');
7897cb960a2Sdownsj 	x_flush();
7907cb960a2Sdownsj 	*xep++ = '\n';
7917cb960a2Sdownsj 	return KEOL;
7927cb960a2Sdownsj }
7937cb960a2Sdownsj 
7947cb960a2Sdownsj static int
x_end_of_text(int c)795c5d5393cSotto x_end_of_text(int c)
7967cb960a2Sdownsj {
797de098c7aSotto 	x_zotc(edchars.eof);
798de098c7aSotto 	x_putc('\r');
799de098c7aSotto 	x_putc('\n');
800de098c7aSotto 	x_flush();
8017cb960a2Sdownsj 	return KEOL;
8027cb960a2Sdownsj }
8037cb960a2Sdownsj 
x_beg_hist(int c)804c5d5393cSotto static int x_beg_hist(int c) { x_load_hist(history); return KSTD;}
8057cb960a2Sdownsj 
x_end_hist(int c)806c5d5393cSotto static int x_end_hist(int c) { x_load_hist(histptr); return KSTD;}
8077cb960a2Sdownsj 
x_prev_com(int c)808c5d5393cSotto static int x_prev_com(int c) { x_load_hist(x_histp - x_arg); return KSTD;}
8097cb960a2Sdownsj 
x_next_com(int c)810c5d5393cSotto static int x_next_com(int c) { x_load_hist(x_histp + x_arg); return KSTD;}
8117cb960a2Sdownsj 
8127cb960a2Sdownsj /* Goto a particular history number obtained from argument.
8137cb960a2Sdownsj  * If no argument is given history 1 is probably not what you
8147cb960a2Sdownsj  * want so we'll simply go to the oldest one.
8157cb960a2Sdownsj  */
8167cb960a2Sdownsj static int
x_goto_hist(int c)817c5d5393cSotto x_goto_hist(int c)
8187cb960a2Sdownsj {
8197cb960a2Sdownsj 	if (x_arg_defaulted)
8207cb960a2Sdownsj 		x_load_hist(history);
8217cb960a2Sdownsj 	else
8227cb960a2Sdownsj 		x_load_hist(histptr + x_arg - source->line);
8237cb960a2Sdownsj 	return KSTD;
8247cb960a2Sdownsj }
8257cb960a2Sdownsj 
8267cb960a2Sdownsj static void
x_load_hist(char ** hp)827c5d5393cSotto x_load_hist(char **hp)
8287cb960a2Sdownsj {
8297cb960a2Sdownsj 	int	oldsize;
8307cb960a2Sdownsj 
8317cb960a2Sdownsj 	if (hp < history || hp > histptr) {
8327cb960a2Sdownsj 		x_e_putc(BEL);
8337cb960a2Sdownsj 		return;
8347cb960a2Sdownsj 	}
8357cb960a2Sdownsj 	x_histp = hp;
8367cb960a2Sdownsj 	oldsize = x_size_str(xbuf);
8371c93b6baStdeval 	strlcpy(xbuf, *hp, xend - xbuf);
8387cb960a2Sdownsj 	xbp = xbuf;
8391c93b6baStdeval 	xep = xcp = xbuf + strlen(xbuf);
8400e7d3a01Smillert 	xlp_valid = false;
8411085a389Sotto 	if (xep <= x_lastcp())
8427cb960a2Sdownsj 		x_redraw(oldsize);
8431085a389Sotto 	x_goto(xep);
8447cb960a2Sdownsj }
8457cb960a2Sdownsj 
8467cb960a2Sdownsj static int
x_nl_next_com(int c)847c5d5393cSotto x_nl_next_com(int c)
8487cb960a2Sdownsj {
8497cb960a2Sdownsj 	x_nextcmd = source->line - (histptr - x_histp) + 1;
8507cb960a2Sdownsj 	return (x_newline(c));
8517cb960a2Sdownsj }
8527cb960a2Sdownsj 
8537cb960a2Sdownsj static int
x_eot_del(int c)854c5d5393cSotto x_eot_del(int c)
8557cb960a2Sdownsj {
8567cb960a2Sdownsj 	if (xep == xbuf && x_arg_defaulted)
8577cb960a2Sdownsj 		return (x_end_of_text(c));
8587cb960a2Sdownsj 	else
8597cb960a2Sdownsj 		return (x_del_char(c));
8607cb960a2Sdownsj }
8617cb960a2Sdownsj 
86228297e90Sjca static kb_func
kb_find_hist_func(char c)863c250457eSdjm kb_find_hist_func(char c)
864c250457eSdjm {
865c250457eSdjm 	struct kb_entry		*k;
866c250457eSdjm 	char			line[LINE + 1];
867c250457eSdjm 
868c250457eSdjm 	line[0] = c;
869c250457eSdjm 	line[1] = '\0';
870c250457eSdjm 	TAILQ_FOREACH(k, &kblist, entry)
871c250457eSdjm 		if (!strcmp(k->seq, line))
872c250457eSdjm 			return (k->ftab->xf_func);
873c250457eSdjm 
874c250457eSdjm 	return (x_insert);
875c250457eSdjm }
876c250457eSdjm 
8777cb960a2Sdownsj /* reverse incremental history search */
8787cb960a2Sdownsj static int
x_search_hist(int c)879c5d5393cSotto x_search_hist(int c)
8807cb960a2Sdownsj {
8817cb960a2Sdownsj 	int offset = -1;	/* offset of match in xbuf, else -1 */
8827cb960a2Sdownsj 	char pat [256+1];	/* pattern buffer */
8837894b443Smillert 	char *p = pat;
884c250457eSdjm 	int (*f)(int);
8857cb960a2Sdownsj 
8867cb960a2Sdownsj 	*p = '\0';
8877cb960a2Sdownsj 	while (1) {
8887cb960a2Sdownsj 		if (offset < 0) {
8897cb960a2Sdownsj 			x_e_puts("\nI-search: ");
8907cb960a2Sdownsj 			x_e_puts(pat);
8917cb960a2Sdownsj 		}
8927cb960a2Sdownsj 		x_flush();
8937cb960a2Sdownsj 		if ((c = x_e_getc()) < 0)
8947cb960a2Sdownsj 			return KSTD;
895c250457eSdjm 		f = kb_find_hist_func(c);
8962a3a8cceShalex 		if (c == CTRL('[') || c == CTRL('@')) {
897a030eb9bSschwarze 			x_e_ungetc(c);
8987cb960a2Sdownsj 			break;
899a030eb9bSschwarze 		} else if (f == x_search_hist)
9007cb960a2Sdownsj 			offset = x_search(pat, 0, offset);
901c250457eSdjm 		else if (f == x_del_back) {
9027cb960a2Sdownsj 			if (p == pat) {
9037cb960a2Sdownsj 				offset = -1;
9047cb960a2Sdownsj 				break;
9057cb960a2Sdownsj 			}
9067cb960a2Sdownsj 			if (p > pat)
9077cb960a2Sdownsj 				*--p = '\0';
9087cb960a2Sdownsj 			if (p == pat)
9097cb960a2Sdownsj 				offset = -1;
9107cb960a2Sdownsj 			else
9117cb960a2Sdownsj 				offset = x_search(pat, 1, offset);
9127cb960a2Sdownsj 			continue;
913c250457eSdjm 		} else if (f == x_insert) {
9147cb960a2Sdownsj 			/* add char to pattern */
9157cb960a2Sdownsj 			/* overflow check... */
9167cb960a2Sdownsj 			if (p >= &pat[sizeof(pat) - 1]) {
9177cb960a2Sdownsj 				x_e_putc(BEL);
9187cb960a2Sdownsj 				continue;
9197cb960a2Sdownsj 			}
9207cb960a2Sdownsj 			*p++ = c, *p = '\0';
9217cb960a2Sdownsj 			if (offset >= 0) {
9227cb960a2Sdownsj 				/* already have partial match */
9237cb960a2Sdownsj 				offset = x_match(xbuf, pat);
9247cb960a2Sdownsj 				if (offset >= 0) {
9257a8124d8Sderaadt 					x_goto(xbuf + offset + (p - pat) -
9267a8124d8Sderaadt 					    (*pat == '^'));
9277cb960a2Sdownsj 					continue;
9287cb960a2Sdownsj 				}
9297cb960a2Sdownsj 			}
9307cb960a2Sdownsj 			offset = x_search(pat, 0, offset);
9317cb960a2Sdownsj 		} else { /* other command */
9327cb960a2Sdownsj 			x_e_ungetc(c);
9337cb960a2Sdownsj 			break;
9347cb960a2Sdownsj 		}
9357cb960a2Sdownsj 	}
9367cb960a2Sdownsj 	if (offset < 0)
9377cb960a2Sdownsj 		x_redraw(-1);
9387cb960a2Sdownsj 	return KSTD;
9397cb960a2Sdownsj }
9407cb960a2Sdownsj 
9417cb960a2Sdownsj /* search backward from current line */
9427cb960a2Sdownsj static int
x_search(char * pat,int sameline,int offset)943c5d5393cSotto x_search(char *pat, int sameline, int offset)
9447cb960a2Sdownsj {
9457894b443Smillert 	char **hp;
9467cb960a2Sdownsj 	int i;
9477cb960a2Sdownsj 
9487cb960a2Sdownsj 	for (hp = x_histp - (sameline ? 0 : 1) ; hp >= history; --hp) {
9497cb960a2Sdownsj 		i = x_match(*hp, pat);
9507cb960a2Sdownsj 		if (i >= 0) {
9517cb960a2Sdownsj 			if (offset < 0)
9527cb960a2Sdownsj 				x_e_putc('\n');
9537cb960a2Sdownsj 			x_load_hist(hp);
9547cb960a2Sdownsj 			x_goto(xbuf + i + strlen(pat) - (*pat == '^'));
9557cb960a2Sdownsj 			return i;
9567cb960a2Sdownsj 		}
9577cb960a2Sdownsj 	}
9587cb960a2Sdownsj 	x_e_putc(BEL);
9597cb960a2Sdownsj 	x_histp = histptr;
9607cb960a2Sdownsj 	return -1;
9617cb960a2Sdownsj }
9627cb960a2Sdownsj 
9637cb960a2Sdownsj /* return position of first match of pattern in string, else -1 */
9647cb960a2Sdownsj static int
x_match(char * str,char * pat)965c5d5393cSotto x_match(char *str, char *pat)
9667cb960a2Sdownsj {
9677cb960a2Sdownsj 	if (*pat == '^') {
9687cb960a2Sdownsj 		return (strncmp(str, pat+1, strlen(pat+1)) == 0) ? 0 : -1;
9697cb960a2Sdownsj 	} else {
9707cb960a2Sdownsj 		char *q = strstr(str, pat);
9717cb960a2Sdownsj 		return (q == NULL) ? -1 : q - str;
9727cb960a2Sdownsj 	}
9737cb960a2Sdownsj }
9747cb960a2Sdownsj 
9757cb960a2Sdownsj static int
x_del_line(int c)976c5d5393cSotto x_del_line(int c)
9777cb960a2Sdownsj {
9787cb960a2Sdownsj 	int	i, j;
9797cb960a2Sdownsj 
9807cb960a2Sdownsj 	*xep = 0;
9817cb960a2Sdownsj 	i = xep - xbuf;
9827cb960a2Sdownsj 	j = x_size_str(xbuf);
9837cb960a2Sdownsj 	xcp = xbuf;
9847cb960a2Sdownsj 	x_push(i);
9857cb960a2Sdownsj 	xlp = xbp = xep = xbuf;
9860e7d3a01Smillert 	xlp_valid = true;
9877cb960a2Sdownsj 	*xcp = 0;
9887cb960a2Sdownsj 	xmp = NULL;
9897cb960a2Sdownsj 	x_redraw(j);
9907cb960a2Sdownsj 	return KSTD;
9917cb960a2Sdownsj }
9927cb960a2Sdownsj 
9937cb960a2Sdownsj static int
x_mv_end(int c)994c5d5393cSotto x_mv_end(int c)
9957cb960a2Sdownsj {
9967cb960a2Sdownsj 	x_goto(xep);
9977cb960a2Sdownsj 	return KSTD;
9987cb960a2Sdownsj }
9997cb960a2Sdownsj 
10007cb960a2Sdownsj static int
x_mv_begin(int c)1001c5d5393cSotto x_mv_begin(int c)
10027cb960a2Sdownsj {
10037cb960a2Sdownsj 	x_goto(xbuf);
10047cb960a2Sdownsj 	return KSTD;
10057cb960a2Sdownsj }
10067cb960a2Sdownsj 
10077cb960a2Sdownsj static int
x_draw_line(int c)1008c5d5393cSotto x_draw_line(int c)
10097cb960a2Sdownsj {
10107cb960a2Sdownsj 	x_redraw(-1);
10117cb960a2Sdownsj 	return KSTD;
10127cb960a2Sdownsj }
10137cb960a2Sdownsj 
1014a3c80d85Smillert static int
x_clear_screen(int c)1015a3c80d85Smillert x_clear_screen(int c)
1016a3c80d85Smillert {
1017a3c80d85Smillert 	x_redraw(-2);
1018a3c80d85Smillert 	return KSTD;
1019a3c80d85Smillert }
1020a3c80d85Smillert 
1021a3c80d85Smillert /* Redraw (part of) the line.
1022a3c80d85Smillert  * A non-negative limit is the screen column up to which needs
1023a3c80d85Smillert  * redrawing. A limit of -1 redraws on a new line, while a limit
1024a3c80d85Smillert  * of -2 (attempts to) clear the screen.
1025dcacb757Sdownsj  */
10267cb960a2Sdownsj static void
x_redraw(int limit)1027c5d5393cSotto x_redraw(int limit)
10287cb960a2Sdownsj {
1029638b9e76Sbeck 	int	i, j, truncate = 0;
10307cb960a2Sdownsj 	char	*cp;
10317cb960a2Sdownsj 
10327cb960a2Sdownsj 	x_adj_ok = 0;
1033a3c80d85Smillert 	if (limit == -2) {
1034a3c80d85Smillert 		int cleared = 0;
1035a3c80d85Smillert #ifndef SMALL
1036a3c80d85Smillert 		if (cur_term != NULL && clear_screen != NULL) {
1037a3c80d85Smillert 			if (tputs(clear_screen, 1, x_putc) != ERR)
1038a3c80d85Smillert 				cleared = 1;
1039a3c80d85Smillert 		}
1040a3c80d85Smillert #endif
1041a3c80d85Smillert 		if (!cleared)
10427cb960a2Sdownsj 			x_e_putc('\n');
1043a3c80d85Smillert 	}
1044a3c80d85Smillert 	else if (limit == -1)
1045a3c80d85Smillert 		x_e_putc('\n');
1046a3c80d85Smillert 	else if (limit >= 0)
10477cb960a2Sdownsj 		x_e_putc('\r');
10487cb960a2Sdownsj 	x_flush();
10497a8124d8Sderaadt 	if (xbp == xbuf) {
1050f7654a50Snicm 		x_col = promptlen(prompt, NULL);
1051638b9e76Sbeck 		if (x_col > xx_cols)
1052638b9e76Sbeck 			truncate = (x_col / xx_cols) * xx_cols;
1053638b9e76Sbeck 		if (prompt_redraw)
1054638b9e76Sbeck 			pprompt(prompt + prompt_skip, truncate);
10557cb960a2Sdownsj 	}
1056638b9e76Sbeck 	if (x_col > xx_cols)
1057638b9e76Sbeck 		x_col = x_col - (x_col / xx_cols) * xx_cols;
1058dcacb757Sdownsj 	x_displen = xx_cols - 2 - x_col;
1059638b9e76Sbeck 	if (x_displen < 1) {
1060638b9e76Sbeck 		x_col = 0;
1061638b9e76Sbeck 		x_displen = xx_cols - 2;
1062638b9e76Sbeck 	}
10630e7d3a01Smillert 	xlp_valid = false;
1064565b7249Sjca 	x_lastcp();
10657cb960a2Sdownsj 	x_zots(xbp);
10667cb960a2Sdownsj 	if (xbp != xbuf || xep > xlp)
1067dcacb757Sdownsj 		limit = xx_cols;
10687a8124d8Sderaadt 	if (limit >= 0) {
10697cb960a2Sdownsj 		if (xep > xlp)
10707cb960a2Sdownsj 			i = 0;			/* we fill the line */
10717cb960a2Sdownsj 		else
10727cb960a2Sdownsj 			i = limit - (xlp - xbp);
10737cb960a2Sdownsj 
1074dcacb757Sdownsj 		for (j = 0; j < i && x_col < (xx_cols - 2); j++)
10757cb960a2Sdownsj 			x_e_putc(' ');
10767cb960a2Sdownsj 		i = ' ';
10777a8124d8Sderaadt 		if (xep > xlp) {		/* more off screen */
10787cb960a2Sdownsj 			if (xbp > xbuf)
10797cb960a2Sdownsj 				i = '*';
10807cb960a2Sdownsj 			else
10817cb960a2Sdownsj 				i = '>';
10827a8124d8Sderaadt 		} else if (xbp > xbuf)
10837cb960a2Sdownsj 			i = '<';
10847cb960a2Sdownsj 		x_e_putc(i);
10857cb960a2Sdownsj 		j++;
10867cb960a2Sdownsj 		while (j--)
10877cb960a2Sdownsj 			x_e_putc('\b');
10887cb960a2Sdownsj 	}
10897cb960a2Sdownsj 	for (cp = xlp; cp > xcp; )
10907cb960a2Sdownsj 		x_bs(*--cp);
10917cb960a2Sdownsj 	x_adj_ok = 1;
10926465bc54Smmcc #ifdef DEBUG
10936465bc54Smmcc 	x_flush();
10946465bc54Smmcc #endif
10957cb960a2Sdownsj 	return;
10967cb960a2Sdownsj }
10977cb960a2Sdownsj 
10987cb960a2Sdownsj static int
x_transpose(int c)1099c5d5393cSotto x_transpose(int c)
11007cb960a2Sdownsj {
11017cb960a2Sdownsj 	char	tmp;
11027cb960a2Sdownsj 
11037cb960a2Sdownsj 	/* What transpose is meant to do seems to be up for debate. This
11047cb960a2Sdownsj 	 * is a general summary of the options; the text is abcd with the
110512e7fb2dSjmc 	 * upper case character or underscore indicating the cursor position:
11067cb960a2Sdownsj 	 *     Who			Before	After  Before	After
11077cb960a2Sdownsj 	 *     at&t ksh in emacs mode:	abCd	abdC   abcd_	(bell)
11087cb960a2Sdownsj 	 *     at&t ksh in gmacs mode:	abCd	baCd   abcd_	abdc_
11097cb960a2Sdownsj 	 *     gnu emacs:		abCd	acbD   abcd_	abdc_
11107cb960a2Sdownsj 	 * Pdksh currently goes with GNU behavior since I believe this is the
11117cb960a2Sdownsj 	 * most common version of emacs, unless in gmacs mode, in which case
1112060cee32Sjmc 	 * it does the at&t ksh gmacs mode.
11137cb960a2Sdownsj 	 * This should really be broken up into 3 functions so users can bind
11147cb960a2Sdownsj 	 * to the one they want.
11157cb960a2Sdownsj 	 */
11167cb960a2Sdownsj 	if (xcp == xbuf) {
11177cb960a2Sdownsj 		x_e_putc(BEL);
11187cb960a2Sdownsj 		return KSTD;
11197cb960a2Sdownsj 	} else if (xcp == xep || Flag(FGMACS)) {
11207cb960a2Sdownsj 		if (xcp - xbuf == 1) {
11217cb960a2Sdownsj 			x_e_putc(BEL);
11227cb960a2Sdownsj 			return KSTD;
11237cb960a2Sdownsj 		}
11247cb960a2Sdownsj 		/* Gosling/Unipress emacs style: Swap two characters before the
11257cb960a2Sdownsj 		 * cursor, do not change cursor position
11267cb960a2Sdownsj 		 */
11277cb960a2Sdownsj 		x_bs(xcp[-1]);
11287cb960a2Sdownsj 		x_bs(xcp[-2]);
11297cb960a2Sdownsj 		x_zotc(xcp[-1]);
11307cb960a2Sdownsj 		x_zotc(xcp[-2]);
11317cb960a2Sdownsj 		tmp = xcp[-1];
11327cb960a2Sdownsj 		xcp[-1] = xcp[-2];
11337cb960a2Sdownsj 		xcp[-2] = tmp;
11347cb960a2Sdownsj 	} else {
11357cb960a2Sdownsj 		/* GNU emacs style: Swap the characters before and under the
11367cb960a2Sdownsj 		 * cursor, move cursor position along one.
11377cb960a2Sdownsj 		 */
11387cb960a2Sdownsj 		x_bs(xcp[-1]);
11397cb960a2Sdownsj 		x_zotc(xcp[0]);
11407cb960a2Sdownsj 		x_zotc(xcp[-1]);
11417cb960a2Sdownsj 		tmp = xcp[-1];
11427cb960a2Sdownsj 		xcp[-1] = xcp[0];
11437cb960a2Sdownsj 		xcp[0] = tmp;
11447cb960a2Sdownsj 		x_bs(xcp[0]);
11457cb960a2Sdownsj 		x_goto(xcp + 1);
11467cb960a2Sdownsj 	}
11477cb960a2Sdownsj 	return KSTD;
11487cb960a2Sdownsj }
11497cb960a2Sdownsj 
11507cb960a2Sdownsj static int
x_literal(int c)1151c5d5393cSotto x_literal(int c)
11527cb960a2Sdownsj {
1153c250457eSdjm 	x_literal_set = 1;
11547cb960a2Sdownsj 	return KSTD;
11557cb960a2Sdownsj }
11567cb960a2Sdownsj 
11577cb960a2Sdownsj static int
x_kill(int c)1158c5d5393cSotto x_kill(int c)
11597cb960a2Sdownsj {
11607cb960a2Sdownsj 	int col = xcp - xbuf;
11617cb960a2Sdownsj 	int lastcol = xep - xbuf;
11627cb960a2Sdownsj 	int ndel;
11637cb960a2Sdownsj 
11647cb960a2Sdownsj 	if (x_arg_defaulted)
11657cb960a2Sdownsj 		x_arg = lastcol;
11667cb960a2Sdownsj 	else if (x_arg > lastcol)
11677cb960a2Sdownsj 		x_arg = lastcol;
11685cc68a1dSschwarze 	while (x_arg < lastcol && isu8cont(xbuf[x_arg]))
11695cc68a1dSschwarze 		x_arg++;
11707cb960a2Sdownsj 	ndel = x_arg - col;
11717cb960a2Sdownsj 	if (ndel < 0) {
11727cb960a2Sdownsj 		x_goto(xbuf + x_arg);
11737cb960a2Sdownsj 		ndel = -ndel;
11747cb960a2Sdownsj 	}
11750e7d3a01Smillert 	x_delete(ndel, true);
11767cb960a2Sdownsj 	return KSTD;
11777cb960a2Sdownsj }
11787cb960a2Sdownsj 
11797cb960a2Sdownsj static void
x_push(int nchars)1180c5d5393cSotto x_push(int nchars)
11817cb960a2Sdownsj {
11827cb960a2Sdownsj 	char	*cp = str_nsave(xcp, nchars, AEDIT);
1183bfd561bcStedu 	afree(killstack[killsp], AEDIT);
11847cb960a2Sdownsj 	killstack[killsp] = cp;
11857cb960a2Sdownsj 	killsp = (killsp + 1) % KILLSIZE;
11867cb960a2Sdownsj }
11877cb960a2Sdownsj 
11887cb960a2Sdownsj static int
x_yank(int c)1189c5d5393cSotto x_yank(int c)
11907cb960a2Sdownsj {
11917cb960a2Sdownsj 	if (killsp == 0)
11927cb960a2Sdownsj 		killtp = KILLSIZE;
11937cb960a2Sdownsj 	else
11947cb960a2Sdownsj 		killtp = killsp;
11957cb960a2Sdownsj 	killtp --;
11967cb960a2Sdownsj 	if (killstack[killtp] == 0) {
11977cb960a2Sdownsj 		x_e_puts("\nnothing to yank");
11987cb960a2Sdownsj 		x_redraw(-1);
11997cb960a2Sdownsj 		return KSTD;
12007cb960a2Sdownsj 	}
12017cb960a2Sdownsj 	xmp = xcp;
12027cb960a2Sdownsj 	x_ins(killstack[killtp]);
12037cb960a2Sdownsj 	return KSTD;
12047cb960a2Sdownsj }
12057cb960a2Sdownsj 
12067cb960a2Sdownsj static int
x_meta_yank(int c)1207c5d5393cSotto x_meta_yank(int c)
12087cb960a2Sdownsj {
12097cb960a2Sdownsj 	int	len;
1210c250457eSdjm 	if ((x_last_command != x_yank && x_last_command != x_meta_yank) ||
12117a8124d8Sderaadt 	    killstack[killtp] == 0) {
1212ab8d2853Sfgsch 		killtp = killsp;
12137cb960a2Sdownsj 		x_e_puts("\nyank something first");
12147cb960a2Sdownsj 		x_redraw(-1);
12157cb960a2Sdownsj 		return KSTD;
12167cb960a2Sdownsj 	}
12177cb960a2Sdownsj 	len = strlen(killstack[killtp]);
12187cb960a2Sdownsj 	x_goto(xcp - len);
12190e7d3a01Smillert 	x_delete(len, false);
12207cb960a2Sdownsj 	do {
12217cb960a2Sdownsj 		if (killtp == 0)
12227cb960a2Sdownsj 			killtp = KILLSIZE - 1;
12237cb960a2Sdownsj 		else
12247cb960a2Sdownsj 			killtp--;
12257cb960a2Sdownsj 	} while (killstack[killtp] == 0);
12267cb960a2Sdownsj 	x_ins(killstack[killtp]);
12277cb960a2Sdownsj 	return KSTD;
12287cb960a2Sdownsj }
12297cb960a2Sdownsj 
12307cb960a2Sdownsj static int
x_abort(int c)1231c5d5393cSotto x_abort(int c)
12327cb960a2Sdownsj {
12337cb960a2Sdownsj 	/* x_zotc(c); */
12347cb960a2Sdownsj 	xlp = xep = xcp = xbp = xbuf;
12350e7d3a01Smillert 	xlp_valid = true;
12367cb960a2Sdownsj 	*xcp = 0;
12377cb960a2Sdownsj 	return KINTR;
12387cb960a2Sdownsj }
12397cb960a2Sdownsj 
12407cb960a2Sdownsj static int
x_error(int c)1241c5d5393cSotto x_error(int c)
12427cb960a2Sdownsj {
12437cb960a2Sdownsj 	x_e_putc(BEL);
12447cb960a2Sdownsj 	return KSTD;
12457cb960a2Sdownsj }
12467cb960a2Sdownsj 
12477cb960a2Sdownsj static char *
kb_encode(const char * s)1248c250457eSdjm kb_encode(const char *s)
12497cb960a2Sdownsj {
1250c250457eSdjm 	static char		l[LINE + 1];
1251c250457eSdjm 	int			at = 0;
12527cb960a2Sdownsj 
1253c250457eSdjm 	l[at] = '\0';
1254c250457eSdjm 	while (*s) {
1255c250457eSdjm 		if (*s == '^') {
1256c250457eSdjm 			s++;
1257c250457eSdjm 			if (*s >= '?')
1258c250457eSdjm 				l[at++] = CTRL(*s);
12597cb960a2Sdownsj 			else {
1260c250457eSdjm 				l[at++] = '^';
1261c250457eSdjm 				s--;
12627cb960a2Sdownsj 			}
12637cb960a2Sdownsj 		} else
1264c250457eSdjm 			l[at++] = *s;
1265c250457eSdjm 		l[at] = '\0';
1266c250457eSdjm 		s++;
12677cb960a2Sdownsj 	}
1268c250457eSdjm 	return (l);
12697cb960a2Sdownsj }
12707cb960a2Sdownsj 
12717cb960a2Sdownsj static char *
kb_decode(const char * s)1272c250457eSdjm kb_decode(const char *s)
12737cb960a2Sdownsj {
1274c250457eSdjm 	static char		l[LINE + 1];
12754c503bcdSmillert 	unsigned int		i, at = 0;
12767cb960a2Sdownsj 
1277c250457eSdjm 	l[0] = '\0';
1278c250457eSdjm 	for (i = 0; i < strlen(s); i++) {
1279a85f8ccfSmmcc 		if (iscntrl((unsigned char)s[i])) {
1280c250457eSdjm 			l[at++] = '^';
1281c250457eSdjm 			l[at++] = UNCTRL(s[i]);
12827cb960a2Sdownsj 		} else
1283c250457eSdjm 			l[at++] = s[i];
1284c250457eSdjm 		l[at] = '\0';
1285c250457eSdjm 	}
1286c250457eSdjm 
1287c250457eSdjm 	return (l);
1288c250457eSdjm }
1289c250457eSdjm 
1290c250457eSdjm static int
kb_match(char * s)1291c250457eSdjm kb_match(char *s)
1292c250457eSdjm {
1293c250457eSdjm 	int			len = strlen(s);
1294c250457eSdjm 	struct kb_entry		*k;
1295c250457eSdjm 
1296c250457eSdjm 	TAILQ_FOREACH(k, &kblist, entry) {
1297c250457eSdjm 		if (len > k->len)
1298c250457eSdjm 			continue;
1299c250457eSdjm 
130089253508Stedu 		if (memcmp(k->seq, s, len) == 0)
1301c250457eSdjm 			return (1);
1302c250457eSdjm 	}
1303c250457eSdjm 
1304c250457eSdjm 	return (0);
13057cb960a2Sdownsj }
13067cb960a2Sdownsj 
13077cb960a2Sdownsj static void
kb_del(struct kb_entry * k)1308c250457eSdjm kb_del(struct kb_entry *k)
13097cb960a2Sdownsj {
1310c250457eSdjm 	TAILQ_REMOVE(&kblist, k, entry);
1311c250457eSdjm 	free(k->args);
1312c250457eSdjm 	afree(k, AEDIT);
1313c250457eSdjm }
1314c250457eSdjm 
1315c250457eSdjm static struct kb_entry *
kb_add_string(kb_func func,void * args,char * str)131628297e90Sjca kb_add_string(kb_func func, void *args, char *str)
1317c250457eSdjm {
13184c503bcdSmillert 	unsigned int		ele, count;
1319c250457eSdjm 	struct kb_entry		*k;
1320c250457eSdjm 	struct x_ftab		*xf = NULL;
1321c250457eSdjm 
13224c503bcdSmillert 	for (ele = 0; ele < NELEM(x_ftab); ele++)
13234c503bcdSmillert 		if (x_ftab[ele].xf_func == func) {
13244c503bcdSmillert 			xf = (struct x_ftab *)&x_ftab[ele];
1325c250457eSdjm 			break;
1326c250457eSdjm 		}
1327c250457eSdjm 	if (xf == NULL)
1328c250457eSdjm 		return (NULL);
1329c250457eSdjm 
1330c250457eSdjm 	if (kb_match(str)) {
1331c250457eSdjm 		if (x_bind_quiet == 0)
1332c250457eSdjm 			bi_errorf("duplicate binding for %s", kb_decode(str));
1333c250457eSdjm 		return (NULL);
1334c250457eSdjm 	}
1335c250457eSdjm 	count = strlen(str);
1336c250457eSdjm 
1337c250457eSdjm 	k = alloc(sizeof *k + count + 1, AEDIT);
1338c250457eSdjm 	k->seq = (unsigned char *)(k + 1);
1339c250457eSdjm 	k->len = count;
1340c250457eSdjm 	k->ftab = xf;
1341c250457eSdjm 	k->args = args ? strdup(args) : NULL;
1342c250457eSdjm 
1343c250457eSdjm 	strlcpy(k->seq, str, count + 1);
1344c250457eSdjm 
1345c250457eSdjm 	TAILQ_INSERT_TAIL(&kblist, k, entry);
1346c250457eSdjm 
1347c250457eSdjm 	return (k);
1348c250457eSdjm }
1349c250457eSdjm 
1350c250457eSdjm static struct kb_entry *
kb_add(kb_func func,...)135128297e90Sjca kb_add(kb_func func, ...)
1352c250457eSdjm {
1353c250457eSdjm 	va_list			ap;
13548234bcc5Smillert 	unsigned char		ch;
13558234bcc5Smillert 	unsigned int		i;
13568234bcc5Smillert 	char			line[LINE + 1];
1357c250457eSdjm 
1358035c6fecSmillert 	va_start(ap, func);
13598234bcc5Smillert 	for (i = 0; i < sizeof(line) - 1; i++) {
13608234bcc5Smillert 		ch = va_arg(ap, unsigned int);
13618234bcc5Smillert 		if (ch == 0)
13628234bcc5Smillert 			break;
13638234bcc5Smillert 		line[i] = ch;
13648234bcc5Smillert 	}
1365c250457eSdjm 	va_end(ap);
13668234bcc5Smillert 	line[i] = '\0';
1367c250457eSdjm 
1368035c6fecSmillert 	return (kb_add_string(func, NULL, line));
1369c250457eSdjm }
1370c250457eSdjm 
1371c250457eSdjm static void
kb_print(struct kb_entry * k)1372c250457eSdjm kb_print(struct kb_entry *k)
1373c250457eSdjm {
1374c250457eSdjm 	if (!(k->ftab->xf_flags & XF_NOBIND))
1375c250457eSdjm 		shprintf("%s = %s\n",
1376c250457eSdjm 		    kb_decode(k->seq), k->ftab->xf_name);
1377c250457eSdjm 	else if (k->args) {
1378c250457eSdjm 		shprintf("%s = ", kb_decode(k->seq));
1379c250457eSdjm 		shprintf("'%s'\n", kb_decode(k->args));
1380c250457eSdjm 	}
13817cb960a2Sdownsj }
13827cb960a2Sdownsj 
13837cb960a2Sdownsj int
x_bind(const char * a1,const char * a2,int macro,int list)1384c5d5393cSotto x_bind(const char *a1, const char *a2,
1385c5d5393cSotto 	int macro,		/* bind -m */
1386c5d5393cSotto 	int list)		/* bind -l */
13877cb960a2Sdownsj {
13884c503bcdSmillert 	unsigned int		i;
1389c250457eSdjm 	struct kb_entry		*k, *kb;
1390c250457eSdjm 	char			in[LINE + 1];
13917cb960a2Sdownsj 
1392c250457eSdjm 	if (x_tty == 0) {
13937cb960a2Sdownsj 		bi_errorf("cannot bind, not a tty");
1394c250457eSdjm 		return (1);
13957cb960a2Sdownsj 	}
13967cb960a2Sdownsj 
13977cb960a2Sdownsj 	if (list) {
1398c250457eSdjm 		/* show all function names */
1399c250457eSdjm 		for (i = 0; i < NELEM(x_ftab); i++) {
1400c250457eSdjm 			if (x_ftab[i].xf_name == NULL)
1401c250457eSdjm 				continue;
1402c250457eSdjm 			if (x_ftab[i].xf_name &&
1403c250457eSdjm 			    !(x_ftab[i].xf_flags & XF_NOBIND))
1404c250457eSdjm 				shprintf("%s\n", x_ftab[i].xf_name);
1405c250457eSdjm 		}
1406c250457eSdjm 		return (0);
14077cb960a2Sdownsj 	}
14087cb960a2Sdownsj 
14097cb960a2Sdownsj 	if (a1 == NULL) {
1410c250457eSdjm 		/* show all bindings */
1411c250457eSdjm 		TAILQ_FOREACH(k, &kblist, entry)
1412c250457eSdjm 			kb_print(k);
1413c250457eSdjm 		return (0);
14147cb960a2Sdownsj 	}
14157cb960a2Sdownsj 
1416c250457eSdjm 	snprintf(in, sizeof in, "%s", kb_encode(a1));
14177cb960a2Sdownsj 	if (a2 == NULL) {
1418c250457eSdjm 		/* print binding */
1419c250457eSdjm 		TAILQ_FOREACH(k, &kblist, entry)
1420c250457eSdjm 			if (!strcmp(k->seq, in)) {
1421c250457eSdjm 				kb_print(k);
1422c250457eSdjm 				return (0);
1423c250457eSdjm 			}
1424c250457eSdjm 		shprintf("%s = %s\n", kb_decode(a1), "auto-insert");
1425c250457eSdjm 		return (0);
14267cb960a2Sdownsj 	}
14277cb960a2Sdownsj 
1428c250457eSdjm 	if (strlen(a2) == 0) {
1429c250457eSdjm 		/* clear binding */
1430c250457eSdjm 		TAILQ_FOREACH_SAFE(k, &kblist, entry, kb)
1431c250457eSdjm 			if (!strcmp(k->seq, in)) {
1432c250457eSdjm 				kb_del(k);
14337cb960a2Sdownsj 				break;
1434c250457eSdjm 			}
1435c250457eSdjm 		return (0);
1436c250457eSdjm 	}
1437c250457eSdjm 
1438c250457eSdjm 	/* set binding */
1439c250457eSdjm 	if (macro) {
1440c250457eSdjm 		/* delete old mapping */
1441c250457eSdjm 		TAILQ_FOREACH_SAFE(k, &kblist, entry, kb)
1442c250457eSdjm 			if (!strcmp(k->seq, in)) {
1443c250457eSdjm 				kb_del(k);
1444c250457eSdjm 				break;
1445c250457eSdjm 			}
1446c250457eSdjm 		kb_add_string(x_ins_string, kb_encode(a2), in);
1447c250457eSdjm 		return (0);
1448c250457eSdjm 	}
1449c250457eSdjm 
1450c250457eSdjm 	/* set non macro binding */
1451c250457eSdjm 	for (i = 0; i < NELEM(x_ftab); i++) {
1452c250457eSdjm 		if (x_ftab[i].xf_name == NULL)
1453c250457eSdjm 			continue;
1454c250457eSdjm 		if (!strcmp(x_ftab[i].xf_name, a2)) {
1455c250457eSdjm 			/* delete old mapping */
1456c250457eSdjm 			TAILQ_FOREACH_SAFE(k, &kblist, entry, kb)
1457c250457eSdjm 				if (!strcmp(k->seq, in)) {
1458c250457eSdjm 					kb_del(k);
1459c250457eSdjm 					break;
1460c250457eSdjm 				}
1461c250457eSdjm 			kb_add_string(x_ftab[i].xf_func, NULL, in);
1462c250457eSdjm 			return (0);
1463c250457eSdjm 		}
1464c250457eSdjm 	}
14657cb960a2Sdownsj 	bi_errorf("%s: no such function", a2);
1466c250457eSdjm 	return (1);
14677cb960a2Sdownsj }
14687cb960a2Sdownsj 
14697cb960a2Sdownsj void
x_init_emacs(void)1470c5d5393cSotto x_init_emacs(void)
14717cb960a2Sdownsj {
1472c250457eSdjm 	x_tty = 1;
14737cb960a2Sdownsj 	ainit(AEDIT);
14747cb960a2Sdownsj 	x_nextcmd = -1;
14757cb960a2Sdownsj 
1476c250457eSdjm 	TAILQ_INIT(&kblist);
1477ee6d79f8Sderaadt 
1478c250457eSdjm 	/* man page order */
1479035c6fecSmillert 	kb_add(x_abort,			CTRL('G'), 0);
1480035c6fecSmillert 	kb_add(x_mv_back,		CTRL('B'), 0);
1481035c6fecSmillert 	kb_add(x_mv_back,		CTRL('X'), CTRL('D'), 0);
1482035c6fecSmillert 	kb_add(x_mv_bword,		CTRL('['), 'b', 0);
1483035c6fecSmillert 	kb_add(x_beg_hist,		CTRL('['), '<', 0);
1484035c6fecSmillert 	kb_add(x_mv_begin,		CTRL('A'), 0);
1485035c6fecSmillert 	kb_add(x_fold_capitalize,	CTRL('['), 'C', 0);
1486035c6fecSmillert 	kb_add(x_fold_capitalize,	CTRL('['), 'c', 0);
1487035c6fecSmillert 	kb_add(x_comment,		CTRL('['), '#', 0);
1488035c6fecSmillert 	kb_add(x_complete,		CTRL('['), CTRL('['), 0);
1489035c6fecSmillert 	kb_add(x_comp_comm,		CTRL('X'), CTRL('['), 0);
1490035c6fecSmillert 	kb_add(x_comp_file,		CTRL('['), CTRL('X'), 0);
1491035c6fecSmillert 	kb_add(x_comp_list,		CTRL('I'), 0);
1492035c6fecSmillert 	kb_add(x_comp_list,		CTRL('['), '=', 0);
1493035c6fecSmillert 	kb_add(x_del_back,		CTRL('?'), 0);
1494035c6fecSmillert 	kb_add(x_del_back,		CTRL('H'), 0);
1495035c6fecSmillert 	kb_add(x_del_char,		CTRL('['), '[', '3', '~', 0); /* delete */
1496035c6fecSmillert 	kb_add(x_del_bword,		CTRL('W'), 0);
1497035c6fecSmillert 	kb_add(x_del_bword,		CTRL('['), CTRL('?'), 0);
1498035c6fecSmillert 	kb_add(x_del_bword,		CTRL('['), CTRL('H'), 0);
1499035c6fecSmillert 	kb_add(x_del_bword,		CTRL('['), 'h', 0);
1500035c6fecSmillert 	kb_add(x_del_fword,		CTRL('['), 'd', 0);
1501035c6fecSmillert 	kb_add(x_next_com,		CTRL('N'), 0);
1502035c6fecSmillert 	kb_add(x_next_com,		CTRL('X'), 'B', 0);
1503035c6fecSmillert 	kb_add(x_fold_lower,		CTRL('['), 'L', 0);
1504035c6fecSmillert 	kb_add(x_fold_lower,		CTRL('['), 'l', 0);
1505035c6fecSmillert 	kb_add(x_end_hist,		CTRL('['), '>', 0);
1506035c6fecSmillert 	kb_add(x_mv_end,		CTRL('E'), 0);
1507c250457eSdjm 	/* how to handle: eot: ^_, underneath copied from original keybindings */
1508035c6fecSmillert 	kb_add(x_end_of_text,		CTRL('_'), 0);
1509035c6fecSmillert 	kb_add(x_eot_del,		CTRL('D'), 0);
1510c250457eSdjm 	/* error */
1511035c6fecSmillert 	kb_add(x_xchg_point_mark,	CTRL('X'), CTRL('X'), 0);
1512035c6fecSmillert 	kb_add(x_expand,		CTRL('['), '*', 0);
1513035c6fecSmillert 	kb_add(x_mv_forw,		CTRL('F'), 0);
1514035c6fecSmillert 	kb_add(x_mv_forw,		CTRL('X'), 'C', 0);
1515035c6fecSmillert 	kb_add(x_mv_fword,		CTRL('['), 'f', 0);
1516035c6fecSmillert 	kb_add(x_goto_hist,		CTRL('['), 'g', 0);
1517c250457eSdjm 	/* kill-line */
1518035c6fecSmillert 	kb_add(x_kill,			CTRL('K'), 0);
1519035c6fecSmillert 	kb_add(x_enumerate,		CTRL('['), '?', 0);
1520035c6fecSmillert 	kb_add(x_list_comm,		CTRL('X'), '?', 0);
1521035c6fecSmillert 	kb_add(x_list_file,		CTRL('X'), CTRL('Y'), 0);
1522035c6fecSmillert 	kb_add(x_newline,		CTRL('J'), 0);
1523035c6fecSmillert 	kb_add(x_newline,		CTRL('M'), 0);
1524035c6fecSmillert 	kb_add(x_nl_next_com,		CTRL('O'), 0);
1525c250457eSdjm 	/* no-op */
1526035c6fecSmillert 	kb_add(x_prev_histword,		CTRL('['), '.', 0);
1527035c6fecSmillert 	kb_add(x_prev_histword,		CTRL('['), '_', 0);
1528c250457eSdjm 	/* how to handle: quote: ^^ */
1529035c6fecSmillert 	kb_add(x_literal,		CTRL('^'), 0);
15301c8c58f6Sjca 	kb_add(x_clear_screen,		CTRL('L'), 0);
1531035c6fecSmillert 	kb_add(x_search_char_back,	CTRL('['), CTRL(']'), 0);
1532035c6fecSmillert 	kb_add(x_search_char_forw,	CTRL(']'), 0);
1533035c6fecSmillert 	kb_add(x_search_hist,		CTRL('R'), 0);
1534035c6fecSmillert 	kb_add(x_set_mark,		CTRL('['), ' ', 0);
1535035c6fecSmillert 	kb_add(x_transpose,		CTRL('T'), 0);
1536035c6fecSmillert 	kb_add(x_prev_com,		CTRL('P'), 0);
1537035c6fecSmillert 	kb_add(x_prev_com,		CTRL('X'), 'A', 0);
1538035c6fecSmillert 	kb_add(x_fold_upper,		CTRL('['), 'U', 0);
1539035c6fecSmillert 	kb_add(x_fold_upper,		CTRL('['), 'u', 0);
1540035c6fecSmillert 	kb_add(x_literal,		CTRL('V'), 0);
1541035c6fecSmillert 	kb_add(x_yank,			CTRL('Y'), 0);
1542035c6fecSmillert 	kb_add(x_meta_yank,		CTRL('['), 'y', 0);
1543c250457eSdjm 	/* man page ends here */
1544f00c5086Smillert 
1545c250457eSdjm 	/* arrow keys */
1546035c6fecSmillert 	kb_add(x_prev_com,		CTRL('['), '[', 'A', 0); /* up */
1547035c6fecSmillert 	kb_add(x_next_com,		CTRL('['), '[', 'B', 0); /* down */
1548035c6fecSmillert 	kb_add(x_mv_forw,		CTRL('['), '[', 'C', 0); /* right */
1549035c6fecSmillert 	kb_add(x_mv_back,		CTRL('['), '[', 'D', 0); /* left */
1550035c6fecSmillert 	kb_add(x_prev_com,		CTRL('['), 'O', 'A', 0); /* up */
1551035c6fecSmillert 	kb_add(x_next_com,		CTRL('['), 'O', 'B', 0); /* down */
1552035c6fecSmillert 	kb_add(x_mv_forw,		CTRL('['), 'O', 'C', 0); /* right */
1553035c6fecSmillert 	kb_add(x_mv_back,		CTRL('['), 'O', 'D', 0); /* left */
1554c250457eSdjm 
1555c250457eSdjm 	/* more navigation keys */
1556035c6fecSmillert 	kb_add(x_mv_begin,		CTRL('['), '[', 'H', 0); /* home */
1557035c6fecSmillert 	kb_add(x_mv_end,		CTRL('['), '[', 'F', 0); /* end */
1558035c6fecSmillert 	kb_add(x_mv_begin,		CTRL('['), 'O', 'H', 0); /* home */
1559035c6fecSmillert 	kb_add(x_mv_end,		CTRL('['), 'O', 'F', 0); /* end */
1560035c6fecSmillert 	kb_add(x_mv_begin,		CTRL('['), '[', '1', '~', 0); /* home */
1561035c6fecSmillert 	kb_add(x_mv_end,		CTRL('['), '[', '4', '~', 0); /* end */
1562035c6fecSmillert 	kb_add(x_mv_begin,		CTRL('['), '[', '7', '~', 0); /* home */
1563035c6fecSmillert 	kb_add(x_mv_end,		CTRL('['), '[', '8', '~', 0); /* end */
1564c250457eSdjm 
1565c250457eSdjm 	/* can't be bound */
1566035c6fecSmillert 	kb_add(x_set_arg,		CTRL('['), '0', 0);
1567035c6fecSmillert 	kb_add(x_set_arg,		CTRL('['), '1', 0);
1568035c6fecSmillert 	kb_add(x_set_arg,		CTRL('['), '2', 0);
1569035c6fecSmillert 	kb_add(x_set_arg,		CTRL('['), '3', 0);
1570035c6fecSmillert 	kb_add(x_set_arg,		CTRL('['), '4', 0);
1571035c6fecSmillert 	kb_add(x_set_arg,		CTRL('['), '5', 0);
1572035c6fecSmillert 	kb_add(x_set_arg,		CTRL('['), '6', 0);
1573035c6fecSmillert 	kb_add(x_set_arg,		CTRL('['), '7', 0);
1574035c6fecSmillert 	kb_add(x_set_arg,		CTRL('['), '8', 0);
1575035c6fecSmillert 	kb_add(x_set_arg,		CTRL('['), '9', 0);
1576c250457eSdjm 
1577c250457eSdjm 	/* ctrl arrow keys */
1578035c6fecSmillert 	kb_add(x_mv_end,		CTRL('['), '[', '1', ';', '5', 'A', 0); /* ctrl up */
1579035c6fecSmillert 	kb_add(x_mv_begin,		CTRL('['), '[', '1', ';', '5', 'B', 0); /* ctrl down */
1580035c6fecSmillert 	kb_add(x_mv_fword,		CTRL('['), '[', '1', ';', '5', 'C', 0); /* ctrl right */
1581035c6fecSmillert 	kb_add(x_mv_bword,		CTRL('['), '[', '1', ';', '5', 'D', 0); /* ctrl left */
1582f00c5086Smillert }
1583f00c5086Smillert 
15847cb960a2Sdownsj void
x_emacs_keys(X_chars * ec)1585c5d5393cSotto x_emacs_keys(X_chars *ec)
15867cb960a2Sdownsj {
1587c250457eSdjm 	x_bind_quiet = 1;
1588f00c5086Smillert 	if (ec->erase >= 0) {
1589035c6fecSmillert 		kb_add(x_del_back, ec->erase, 0);
1590035c6fecSmillert 		kb_add(x_del_bword, CTRL('['), ec->erase, 0);
1591f00c5086Smillert 	}
1592f00c5086Smillert 	if (ec->kill >= 0)
1593035c6fecSmillert 		kb_add(x_del_line, ec->kill, 0);
1594f00c5086Smillert 	if (ec->werase >= 0)
1595035c6fecSmillert 		kb_add(x_del_bword, ec->werase, 0);
1596f00c5086Smillert 	if (ec->intr >= 0)
1597035c6fecSmillert 		kb_add(x_abort, ec->intr, 0);
1598f00c5086Smillert 	if (ec->quit >= 0)
1599035c6fecSmillert 		kb_add(x_noop, ec->quit, 0);
1600c250457eSdjm 	x_bind_quiet = 0;
16017cb960a2Sdownsj }
16027cb960a2Sdownsj 
16037cb960a2Sdownsj static int
x_set_mark(int c)1604c5d5393cSotto x_set_mark(int c)
16057cb960a2Sdownsj {
16067cb960a2Sdownsj 	xmp = xcp;
16077cb960a2Sdownsj 	return KSTD;
16087cb960a2Sdownsj }
16097cb960a2Sdownsj 
16107cb960a2Sdownsj static int
x_kill_region(int c)1611c5d5393cSotto x_kill_region(int c)
16127cb960a2Sdownsj {
16137cb960a2Sdownsj 	int	rsize;
16147cb960a2Sdownsj 	char	*xr;
16157cb960a2Sdownsj 
16167cb960a2Sdownsj 	if (xmp == NULL) {
16177cb960a2Sdownsj 		x_e_putc(BEL);
16187cb960a2Sdownsj 		return KSTD;
16197cb960a2Sdownsj 	}
16207cb960a2Sdownsj 	if (xmp > xcp) {
16217cb960a2Sdownsj 		rsize = xmp - xcp;
16227cb960a2Sdownsj 		xr = xcp;
16237cb960a2Sdownsj 	} else {
16247cb960a2Sdownsj 		rsize = xcp - xmp;
16257cb960a2Sdownsj 		xr = xmp;
16267cb960a2Sdownsj 	}
16277cb960a2Sdownsj 	x_goto(xr);
16280e7d3a01Smillert 	x_delete(rsize, true);
16297cb960a2Sdownsj 	xmp = xr;
16307cb960a2Sdownsj 	return KSTD;
16317cb960a2Sdownsj }
16327cb960a2Sdownsj 
16337cb960a2Sdownsj static int
x_xchg_point_mark(int c)1634c5d5393cSotto x_xchg_point_mark(int c)
16357cb960a2Sdownsj {
16367cb960a2Sdownsj 	char	*tmp;
16377cb960a2Sdownsj 
16387cb960a2Sdownsj 	if (xmp == NULL) {
16397cb960a2Sdownsj 		x_e_putc(BEL);
16407cb960a2Sdownsj 		return KSTD;
16417cb960a2Sdownsj 	}
16427cb960a2Sdownsj 	tmp = xmp;
16437cb960a2Sdownsj 	xmp = xcp;
16447cb960a2Sdownsj 	x_goto( tmp );
16457cb960a2Sdownsj 	return KSTD;
16467cb960a2Sdownsj }
16477cb960a2Sdownsj 
16487cb960a2Sdownsj static int
x_noop(int c)1649c5d5393cSotto x_noop(int c)
16507cb960a2Sdownsj {
16517cb960a2Sdownsj 	return KSTD;
16527cb960a2Sdownsj }
16537cb960a2Sdownsj 
16547cb960a2Sdownsj /*
16557cb960a2Sdownsj  *	File/command name completion routines
16567cb960a2Sdownsj  */
16577cb960a2Sdownsj 
16587cb960a2Sdownsj static int
x_comp_comm(int c)1659c5d5393cSotto x_comp_comm(int c)
16607cb960a2Sdownsj {
16617cb960a2Sdownsj 	do_complete(XCF_COMMAND, CT_COMPLETE);
16627cb960a2Sdownsj 	return KSTD;
16637cb960a2Sdownsj }
16647cb960a2Sdownsj static int
x_list_comm(int c)1665c5d5393cSotto x_list_comm(int c)
16667cb960a2Sdownsj {
16677cb960a2Sdownsj 	do_complete(XCF_COMMAND, CT_LIST);
16687cb960a2Sdownsj 	return KSTD;
16697cb960a2Sdownsj }
16707cb960a2Sdownsj static int
x_complete(int c)1671c5d5393cSotto x_complete(int c)
16727cb960a2Sdownsj {
16737cb960a2Sdownsj 	do_complete(XCF_COMMAND_FILE, CT_COMPLETE);
16747cb960a2Sdownsj 	return KSTD;
16757cb960a2Sdownsj }
16767cb960a2Sdownsj static int
x_enumerate(int c)1677c5d5393cSotto x_enumerate(int c)
16787cb960a2Sdownsj {
16797cb960a2Sdownsj 	do_complete(XCF_COMMAND_FILE, CT_LIST);
16807cb960a2Sdownsj 	return KSTD;
16817cb960a2Sdownsj }
16827cb960a2Sdownsj static int
x_comp_file(int c)1683c5d5393cSotto x_comp_file(int c)
16847cb960a2Sdownsj {
16857cb960a2Sdownsj 	do_complete(XCF_FILE, CT_COMPLETE);
16867cb960a2Sdownsj 	return KSTD;
16877cb960a2Sdownsj }
16887cb960a2Sdownsj static int
x_list_file(int c)1689c5d5393cSotto x_list_file(int c)
16907cb960a2Sdownsj {
16917cb960a2Sdownsj 	do_complete(XCF_FILE, CT_LIST);
16927cb960a2Sdownsj 	return KSTD;
16937cb960a2Sdownsj }
16947cb960a2Sdownsj static int
x_comp_list(int c)1695c5d5393cSotto x_comp_list(int c)
16967cb960a2Sdownsj {
16977cb960a2Sdownsj 	do_complete(XCF_COMMAND_FILE, CT_COMPLIST);
16987cb960a2Sdownsj 	return KSTD;
16997cb960a2Sdownsj }
17007cb960a2Sdownsj static int
x_expand(int c)1701c5d5393cSotto x_expand(int c)
17027cb960a2Sdownsj {
17037cb960a2Sdownsj 	char **words;
17047cb960a2Sdownsj 	int nwords = 0;
17057cb960a2Sdownsj 	int start, end;
17067cb960a2Sdownsj 	int is_command;
17077cb960a2Sdownsj 	int i;
17087cb960a2Sdownsj 
17097a8124d8Sderaadt 	nwords = x_cf_glob(XCF_FILE, xbuf, xep - xbuf, xcp - xbuf,
17107cb960a2Sdownsj 	    &start, &end, &words, &is_command);
17117cb960a2Sdownsj 
17127cb960a2Sdownsj 	if (nwords == 0) {
17137cb960a2Sdownsj 		x_e_putc(BEL);
17147cb960a2Sdownsj 		return KSTD;
17157cb960a2Sdownsj 	}
17167cb960a2Sdownsj 
17177cb960a2Sdownsj 	x_goto(xbuf + start);
17180e7d3a01Smillert 	x_delete(end - start, false);
1719bd21a13fSfgsch 	for (i = 0; i < nwords;) {
17209ecf7f57Smillert 		if (x_escape(words[i], strlen(words[i]), x_do_ins) < 0 ||
172196085982Snicm 		    (++i < nwords && x_ins(" ") < 0)) {
17227cb960a2Sdownsj 			x_e_putc(BEL);
17237cb960a2Sdownsj 			return KSTD;
17247cb960a2Sdownsj 		}
1725bd21a13fSfgsch 	}
1726bd21a13fSfgsch 	x_adjust();
17277cb960a2Sdownsj 
17287cb960a2Sdownsj 	return KSTD;
17297cb960a2Sdownsj }
17307cb960a2Sdownsj 
17317cb960a2Sdownsj /* type == 0 for list, 1 for complete and 2 for complete-list */
17327cb960a2Sdownsj static void
do_complete(int flags,Comp_type type)1733c5d5393cSotto do_complete(int flags,	/* XCF_{COMMAND,FILE,COMMAND_FILE} */
1734c5d5393cSotto     Comp_type type)
17357cb960a2Sdownsj {
17367cb960a2Sdownsj 	char **words;
17379ca598aeScamield 	int nwords;
17389ca598aeScamield 	int start, end, nlen, olen;
17397cb960a2Sdownsj 	int is_command;
17409ca598aeScamield 	int completed = 0;
17417cb960a2Sdownsj 
17429ca598aeScamield 	nwords = x_cf_glob(flags, xbuf, xep - xbuf, xcp - xbuf,
17437cb960a2Sdownsj 	    &start, &end, &words, &is_command);
17449ca598aeScamield 	/* no match */
17457cb960a2Sdownsj 	if (nwords == 0) {
17467cb960a2Sdownsj 		x_e_putc(BEL);
17477cb960a2Sdownsj 		return;
17487cb960a2Sdownsj 	}
17499ca598aeScamield 
17509ca598aeScamield 	if (type == CT_LIST) {
17517cb960a2Sdownsj 		x_print_expansions(nwords, words, is_command);
17527cb960a2Sdownsj 		x_redraw(0);
17539ca598aeScamield 		x_free_words(nwords, words);
17549ca598aeScamield 		return;
17557cb960a2Sdownsj 	}
17567cb960a2Sdownsj 
17579ca598aeScamield 	olen = end - start;
17589ca598aeScamield 	nlen = x_longest_prefix(nwords, words);
1759f0c97affScamield 	/* complete */
1760de04cf22Sfgsch 	if (nwords == 1 || nlen > olen) {
17617cb960a2Sdownsj 		x_goto(xbuf + start);
17620e7d3a01Smillert 		x_delete(olen, false);
17639ecf7f57Smillert 		x_escape(words[0], nlen, x_do_ins);
1764953559d8Sd 		x_adjust();
17659ca598aeScamield 		completed = 1;
17669ca598aeScamield 	}
1767f0c97affScamield 	/* add space if single non-dir match */
176869b9f96bSmillert 	if (nwords == 1 && words[0][nlen - 1] != '/') {
176996085982Snicm 		x_ins(" ");
17709ca598aeScamield 		completed = 1;
17717cb960a2Sdownsj 	}
17729ca598aeScamield 
17739ca598aeScamield 	if (type == CT_COMPLIST && !completed) {
17749ca598aeScamield 		x_print_expansions(nwords, words, is_command);
17759ca598aeScamield 		completed = 1;
17767cb960a2Sdownsj 	}
17779ca598aeScamield 
17789ca598aeScamield 	if (completed)
17799ca598aeScamield 		x_redraw(0);
17809ca598aeScamield 
17819ca598aeScamield 	x_free_words(nwords, words);
17827cb960a2Sdownsj }
17837cb960a2Sdownsj 
17847cb960a2Sdownsj /* NAME:
17857cb960a2Sdownsj  *      x_adjust - redraw the line adjusting starting point etc.
17867cb960a2Sdownsj  *
17877cb960a2Sdownsj  * DESCRIPTION:
17887cb960a2Sdownsj  *      This function is called when we have exceeded the bounds
17897cb960a2Sdownsj  *      of the edit window.  It increments x_adj_done so that
17907cb960a2Sdownsj  *      functions like x_ins and x_delete know that we have been
17917cb960a2Sdownsj  *      called and can skip the x_bs() stuff which has already
17927cb960a2Sdownsj  *      been done by x_redraw.
17937cb960a2Sdownsj  *
17947cb960a2Sdownsj  * RETURN VALUE:
17957cb960a2Sdownsj  *      None
17967cb960a2Sdownsj  */
17977cb960a2Sdownsj 
17987cb960a2Sdownsj static void
x_adjust(void)1799c5d5393cSotto x_adjust(void)
18007cb960a2Sdownsj {
18017cb960a2Sdownsj 	x_adj_done++;			/* flag the fact that we were called. */
18027cb960a2Sdownsj 	/*
1803dcacb757Sdownsj 	 * we had a problem if the prompt length > xx_cols / 2
18047cb960a2Sdownsj 	 */
18057cb960a2Sdownsj 	if ((xbp = xcp - (x_displen / 2)) < xbuf)
18067cb960a2Sdownsj 		xbp = xbuf;
18070e7d3a01Smillert 	xlp_valid = false;
1808dcacb757Sdownsj 	x_redraw(xx_cols);
18097cb960a2Sdownsj 	x_flush();
18107cb960a2Sdownsj }
18117cb960a2Sdownsj 
18127cb960a2Sdownsj static int unget_char = -1;
18137cb960a2Sdownsj 
18147cb960a2Sdownsj static void
x_e_ungetc(int c)1815c5d5393cSotto x_e_ungetc(int c)
18167cb960a2Sdownsj {
18177cb960a2Sdownsj 	unget_char = c;
18187cb960a2Sdownsj }
18197cb960a2Sdownsj 
18207cb960a2Sdownsj static int
x_e_getc(void)1821c5d5393cSotto x_e_getc(void)
18227cb960a2Sdownsj {
18237cb960a2Sdownsj 	int c;
18247cb960a2Sdownsj 
18257cb960a2Sdownsj 	if (unget_char >= 0) {
18267cb960a2Sdownsj 		c = unget_char;
18277cb960a2Sdownsj 		unget_char = -1;
182844d96bb9Smpi 	} else if (macro_args) {
182944d96bb9Smpi 		c = *macro_args++;
183044d96bb9Smpi 		if (!c) {
183144d96bb9Smpi 			macro_args = NULL;
183244d96bb9Smpi 			c = x_getc();
183344d96bb9Smpi 		}
18347cb960a2Sdownsj 	} else
18357cb960a2Sdownsj 		c = x_getc();
18367cb960a2Sdownsj 
1837c250457eSdjm 	return c;
18387cb960a2Sdownsj }
18397cb960a2Sdownsj 
18408a9faaaaSanton static int
x_e_getu8(char * buf,int off)18418a9faaaaSanton x_e_getu8(char *buf, int off)
18428a9faaaaSanton {
18438a9faaaaSanton 	int	c, cc, len;
18448a9faaaaSanton 
18458a9faaaaSanton 	c = x_e_getc();
18468a9faaaaSanton 	if (c == -1)
18478a9faaaaSanton 		return -1;
18488a9faaaaSanton 	buf[off++] = c;
18498a9faaaaSanton 
18509704c0f8Sschwarze 	/*
18519704c0f8Sschwarze 	 * In the following, comments refer to violations of
18529704c0f8Sschwarze 	 * the inequality tests at the ends of the lines.
18539704c0f8Sschwarze 	 * See the utf8(7) manual page for details.
18549704c0f8Sschwarze 	 */
18559704c0f8Sschwarze 
18569704c0f8Sschwarze 	if ((c & 0xf8) == 0xf0 && c < 0xf5)  /* beyond Unicode */
18578a9faaaaSanton 		len = 4;
18588a9faaaaSanton 	else if ((c & 0xf0) == 0xe0)
18598a9faaaaSanton 		len = 3;
18609704c0f8Sschwarze 	else if ((c & 0xe0) == 0xc0 && c > 0xc1)  /* use single byte */
18618a9faaaaSanton 		len = 2;
18628a9faaaaSanton 	else
18638a9faaaaSanton 		len = 1;
18648a9faaaaSanton 
18658a9faaaaSanton 	for (; len > 1; len--) {
18668a9faaaaSanton 		cc = x_e_getc();
18678a9faaaaSanton 		if (cc == -1)
18688a9faaaaSanton 			break;
18698a9faaaaSanton 		if (isu8cont(cc) == 0 ||
18709704c0f8Sschwarze 		    (c == 0xe0 && len == 3 && cc < 0xa0) ||  /* use 2 bytes */
18719704c0f8Sschwarze 		    (c == 0xed && len == 3 && cc > 0x9f) ||  /* surrogates  */
18729704c0f8Sschwarze 		    (c == 0xf0 && len == 4 && cc < 0x90) ||  /* use 3 bytes */
18739704c0f8Sschwarze 		    (c == 0xf4 && len == 4 && cc > 0x8f)) {  /* beyond Uni. */
18748a9faaaaSanton 			x_e_ungetc(cc);
18758a9faaaaSanton 			break;
18768a9faaaaSanton 		}
18778a9faaaaSanton 		buf[off++] = cc;
18788a9faaaaSanton 	}
18798a9faaaaSanton 	buf[off] = '\0';
18808a9faaaaSanton 
18818a9faaaaSanton 	return off;
18828a9faaaaSanton }
18838a9faaaaSanton 
18847cb960a2Sdownsj static void
x_e_putc(int c)1885c5d5393cSotto x_e_putc(int c)
18867cb960a2Sdownsj {
18877cb960a2Sdownsj 	if (c == '\r' || c == '\n')
18887cb960a2Sdownsj 		x_col = 0;
18897a8124d8Sderaadt 	if (x_col < xx_cols) {
18907cb960a2Sdownsj 		x_putc(c);
18917a8124d8Sderaadt 		switch (c) {
18927cb960a2Sdownsj 		case BEL:
18937cb960a2Sdownsj 			break;
18947cb960a2Sdownsj 		case '\r':
18957cb960a2Sdownsj 		case '\n':
18967cb960a2Sdownsj 			break;
18977cb960a2Sdownsj 		case '\b':
18987cb960a2Sdownsj 			x_col--;
18997cb960a2Sdownsj 			break;
19007cb960a2Sdownsj 		default:
19013e173141Sschwarze 			if (!isu8cont(c))
19027cb960a2Sdownsj 				x_col++;
19037cb960a2Sdownsj 			break;
19047cb960a2Sdownsj 		}
19057cb960a2Sdownsj 	}
1906dcacb757Sdownsj 	if (x_adj_ok && (x_col < 0 || x_col >= (xx_cols - 2)))
19077cb960a2Sdownsj 		x_adjust();
19087cb960a2Sdownsj }
19097cb960a2Sdownsj 
19107cb960a2Sdownsj #ifdef DEBUG
19117cb960a2Sdownsj static int
x_debug_info(int c)1912c5d5393cSotto x_debug_info(int c)
19137cb960a2Sdownsj {
19147cb960a2Sdownsj 	x_flush();
1915dcacb757Sdownsj 	shellf("\nksh debug:\n");
1916dcacb757Sdownsj 	shellf("\tx_col == %d,\t\tx_cols == %d,\tx_displen == %d\n",
1917dcacb757Sdownsj 	    x_col, xx_cols, x_displen);
1918dcacb757Sdownsj 	shellf("\txcp == 0x%lx,\txep == 0x%lx\n", (long) xcp, (long) xep);
1919dcacb757Sdownsj 	shellf("\txbp == 0x%lx,\txbuf == 0x%lx\n", (long) xbp, (long) xbuf);
1920dcacb757Sdownsj 	shellf("\txlp == 0x%lx\n", (long) xlp);
1921dcacb757Sdownsj 	shellf("\txlp == 0x%lx\n", (long) x_lastcp());
192296085982Snicm 	shellf("\n");
19237cb960a2Sdownsj 	x_redraw(-1);
19247cb960a2Sdownsj 	return 0;
19257cb960a2Sdownsj }
19267cb960a2Sdownsj #endif
19277cb960a2Sdownsj 
19287cb960a2Sdownsj static void
x_e_puts(const char * s)1929c5d5393cSotto x_e_puts(const char *s)
19307cb960a2Sdownsj {
19317894b443Smillert 	int	adj = x_adj_done;
19327cb960a2Sdownsj 
19337cb960a2Sdownsj 	while (*s && adj == x_adj_done)
19347cb960a2Sdownsj 		x_e_putc(*s++);
19357cb960a2Sdownsj }
19367cb960a2Sdownsj 
19377cb960a2Sdownsj /* NAME:
19387cb960a2Sdownsj  *      x_set_arg - set an arg value for next function
19397cb960a2Sdownsj  *
19407cb960a2Sdownsj  * DESCRIPTION:
19417cb960a2Sdownsj  *      This is a simple implementation of M-[0-9].
19427cb960a2Sdownsj  *
19437cb960a2Sdownsj  * RETURN VALUE:
19447cb960a2Sdownsj  *      KSTD
19457cb960a2Sdownsj  */
19467cb960a2Sdownsj 
19477cb960a2Sdownsj static int
x_set_arg(int c)1948c5d5393cSotto x_set_arg(int c)
19497cb960a2Sdownsj {
19507cb960a2Sdownsj 	int n = 0;
19517cb960a2Sdownsj 	int first = 1;
19527cb960a2Sdownsj 
1953c250457eSdjm 	for (; c >= 0 && isdigit(c); c = x_e_getc(), first = 0)
19547cb960a2Sdownsj 		n = n * 10 + (c - '0');
19557cb960a2Sdownsj 	if (c < 0 || first) {
19567cb960a2Sdownsj 		x_e_putc(BEL);
19577cb960a2Sdownsj 		x_arg = 1;
19587cb960a2Sdownsj 		x_arg_defaulted = 1;
19597cb960a2Sdownsj 	} else {
19607cb960a2Sdownsj 		x_e_ungetc(c);
19617cb960a2Sdownsj 		x_arg = n;
19627cb960a2Sdownsj 		x_arg_defaulted = 0;
1963c250457eSdjm 		x_arg_set = 1;
19647cb960a2Sdownsj 	}
19657cb960a2Sdownsj 	return KSTD;
19667cb960a2Sdownsj }
19677cb960a2Sdownsj 
19687cb960a2Sdownsj 
1969dcacb757Sdownsj /* Comment or uncomment the current line. */
1970dcacb757Sdownsj static int
x_comment(int c)1971c5d5393cSotto x_comment(int c)
1972dcacb757Sdownsj {
1973dcacb757Sdownsj 	int oldsize = x_size_str(xbuf);
1974dcacb757Sdownsj 	int len = xep - xbuf;
1975dcacb757Sdownsj 	int ret = x_do_comment(xbuf, xend - xbuf, &len);
1976dcacb757Sdownsj 
1977dcacb757Sdownsj 	if (ret < 0)
1978dcacb757Sdownsj 		x_e_putc(BEL);
1979dcacb757Sdownsj 	else {
1980dcacb757Sdownsj 		xep = xbuf + len;
1981dcacb757Sdownsj 		*xep = '\0';
1982dcacb757Sdownsj 		xcp = xbp = xbuf;
1983dcacb757Sdownsj 		x_redraw(oldsize);
1984dcacb757Sdownsj 		if (ret > 0)
1985dcacb757Sdownsj 			return x_newline('\n');
1986dcacb757Sdownsj 	}
1987dcacb757Sdownsj 	return KSTD;
1988dcacb757Sdownsj }
1989dcacb757Sdownsj 
1990dcacb757Sdownsj 
19917cb960a2Sdownsj /* NAME:
19927cb960a2Sdownsj  *      x_prev_histword - recover word from prev command
19937cb960a2Sdownsj  *
19947cb960a2Sdownsj  * DESCRIPTION:
19957cb960a2Sdownsj  *      This function recovers the last word from the previous
19967cb960a2Sdownsj  *      command and inserts it into the current edit line.  If a
19977cb960a2Sdownsj  *      numeric arg is supplied then the n'th word from the
19987cb960a2Sdownsj  *      start of the previous command is used.
19997cb960a2Sdownsj  *
20007cb960a2Sdownsj  *      Bound to M-.
20017cb960a2Sdownsj  *
20027cb960a2Sdownsj  * RETURN VALUE:
20037cb960a2Sdownsj  *      KSTD
20047cb960a2Sdownsj  */
20057cb960a2Sdownsj 
20067cb960a2Sdownsj static int
x_prev_histword(int c)2007c5d5393cSotto x_prev_histword(int c)
20087cb960a2Sdownsj {
20097894b443Smillert 	char *rcp;
20107cb960a2Sdownsj 	char *cp;
20117cb960a2Sdownsj 
20123b015934Smillert 	cp = *histptr;
2013040161f7Smillert 	if (!cp)
2014040161f7Smillert 		x_e_putc(BEL);
2015040161f7Smillert 	else if (x_arg_defaulted) {
20167cb960a2Sdownsj 		rcp = &cp[strlen(cp) - 1];
20177cb960a2Sdownsj 		/*
20187cb960a2Sdownsj 		 * ignore white-space after the last word
20197cb960a2Sdownsj 		 */
20207cb960a2Sdownsj 		while (rcp > cp && is_cfs(*rcp))
20217cb960a2Sdownsj 			rcp--;
20227cb960a2Sdownsj 		while (rcp > cp && !is_cfs(*rcp))
20237cb960a2Sdownsj 			rcp--;
20247cb960a2Sdownsj 		if (is_cfs(*rcp))
20257cb960a2Sdownsj 			rcp++;
20267cb960a2Sdownsj 		x_ins(rcp);
20277cb960a2Sdownsj 	} else {
20287cb960a2Sdownsj 		rcp = cp;
20297cb960a2Sdownsj 		/*
20307cb960a2Sdownsj 		 * ignore white-space at start of line
20317cb960a2Sdownsj 		 */
20327cb960a2Sdownsj 		while (*rcp && is_cfs(*rcp))
20337cb960a2Sdownsj 			rcp++;
20347894b443Smillert 		while (x_arg-- > 1) {
20357cb960a2Sdownsj 			while (*rcp && !is_cfs(*rcp))
20367cb960a2Sdownsj 				rcp++;
20377cb960a2Sdownsj 			while (*rcp && is_cfs(*rcp))
20387cb960a2Sdownsj 				rcp++;
20397cb960a2Sdownsj 		}
20407cb960a2Sdownsj 		cp = rcp;
20417cb960a2Sdownsj 		while (*rcp && !is_cfs(*rcp))
20427cb960a2Sdownsj 			rcp++;
20437cb960a2Sdownsj 		c = *rcp;
20447cb960a2Sdownsj 		*rcp = '\0';
20457cb960a2Sdownsj 		x_ins(cp);
20467cb960a2Sdownsj 		*rcp = c;
20477cb960a2Sdownsj 	}
20487cb960a2Sdownsj 	return KSTD;
20497cb960a2Sdownsj }
20507cb960a2Sdownsj 
20517cb960a2Sdownsj /* Uppercase N(1) words */
20527cb960a2Sdownsj static int
x_fold_upper(int c)2053c5d5393cSotto x_fold_upper(int c)
20547cb960a2Sdownsj {
20557cb960a2Sdownsj 	return x_fold_case('U');
20567cb960a2Sdownsj }
20577cb960a2Sdownsj 
20587cb960a2Sdownsj /* Lowercase N(1) words */
20597cb960a2Sdownsj static int
x_fold_lower(int c)2060c5d5393cSotto x_fold_lower(int c)
20617cb960a2Sdownsj {
20627cb960a2Sdownsj 	return x_fold_case('L');
20637cb960a2Sdownsj }
20647cb960a2Sdownsj 
20657cb960a2Sdownsj /* Lowercase N(1) words */
20667cb960a2Sdownsj static int
x_fold_capitalize(int c)2067c5d5393cSotto x_fold_capitalize(int c)
20687cb960a2Sdownsj {
20697cb960a2Sdownsj 	return x_fold_case('C');
20707cb960a2Sdownsj }
20717cb960a2Sdownsj 
20727cb960a2Sdownsj /* NAME:
207312e7fb2dSjmc  *      x_fold_case - convert word to UPPER/lower/Capital case
20747cb960a2Sdownsj  *
20757cb960a2Sdownsj  * DESCRIPTION:
20767cb960a2Sdownsj  *      This function is used to implement M-U,M-u,M-L,M-l,M-C and M-c
20777cb960a2Sdownsj  *      to UPPER case, lower case or Capitalize words.
20787cb960a2Sdownsj  *
20797cb960a2Sdownsj  * RETURN VALUE:
20807cb960a2Sdownsj  *      None
20817cb960a2Sdownsj  */
20827cb960a2Sdownsj 
20837cb960a2Sdownsj static int
x_fold_case(int c)2084c5d5393cSotto x_fold_case(int c)
20857cb960a2Sdownsj {
20867cb960a2Sdownsj 	char *cp = xcp;
20877cb960a2Sdownsj 
20887cb960a2Sdownsj 	if (cp == xep) {
20897cb960a2Sdownsj 		x_e_putc(BEL);
20907cb960a2Sdownsj 		return KSTD;
20917cb960a2Sdownsj 	}
20927cb960a2Sdownsj 	while (x_arg--) {
20937cb960a2Sdownsj 		/*
2094060cee32Sjmc 		 * first skip over any white-space
20957cb960a2Sdownsj 		 */
20967cb960a2Sdownsj 		while (cp != xep && is_mfs(*cp))
20977cb960a2Sdownsj 			cp++;
20987cb960a2Sdownsj 		/*
20997cb960a2Sdownsj 		 * do the first char on its own since it may be
21007cb960a2Sdownsj 		 * a different action than for the rest.
21017cb960a2Sdownsj 		 */
21027cb960a2Sdownsj 		if (cp != xep) {
21037cb960a2Sdownsj 			if (c == 'L') {		/* lowercase */
2104e569fc7cSderaadt 				if (isupper((unsigned char)*cp))
2105e569fc7cSderaadt 					*cp = tolower((unsigned char)*cp);
2106060cee32Sjmc 			} else {		/* uppercase, capitalize */
2107e569fc7cSderaadt 				if (islower((unsigned char)*cp))
2108e569fc7cSderaadt 					*cp = toupper((unsigned char)*cp);
21097cb960a2Sdownsj 			}
21107cb960a2Sdownsj 			cp++;
21117cb960a2Sdownsj 		}
21127cb960a2Sdownsj 		/*
21137cb960a2Sdownsj 		 * now for the rest of the word
21147cb960a2Sdownsj 		 */
21157cb960a2Sdownsj 		while (cp != xep && !is_mfs(*cp)) {
21167cb960a2Sdownsj 			if (c == 'U') {		/* uppercase */
2117e569fc7cSderaadt 				if (islower((unsigned char)*cp))
2118e569fc7cSderaadt 					*cp = toupper((unsigned char)*cp);
2119060cee32Sjmc 			} else {		/* lowercase, capitalize */
2120e569fc7cSderaadt 				if (isupper((unsigned char)*cp))
2121e569fc7cSderaadt 					*cp = tolower((unsigned char)*cp);
21227cb960a2Sdownsj 			}
21237cb960a2Sdownsj 			cp++;
21247cb960a2Sdownsj 		}
21257cb960a2Sdownsj 	}
21267cb960a2Sdownsj 	x_goto(cp);
21277cb960a2Sdownsj 	return KSTD;
21287cb960a2Sdownsj }
21297cb960a2Sdownsj 
21307cb960a2Sdownsj /* NAME:
21315cc68a1dSschwarze  *      x_lastcp - last visible byte
21327cb960a2Sdownsj  *
21337cb960a2Sdownsj  * SYNOPSIS:
21347cb960a2Sdownsj  *      x_lastcp()
21357cb960a2Sdownsj  *
21367cb960a2Sdownsj  * DESCRIPTION:
21375cc68a1dSschwarze  *      This function returns a pointer to that byte in the
21387cb960a2Sdownsj  *      edit buffer that will be the last displayed on the
21397cb960a2Sdownsj  *      screen.  The sequence:
21407cb960a2Sdownsj  *
21417cb960a2Sdownsj  *      for (cp = x_lastcp(); cp > xcp; cp)
21427cb960a2Sdownsj  *        x_bs(*--cp);
21437cb960a2Sdownsj  *
21447cb960a2Sdownsj  *      Will position the cursor correctly on the screen.
21457cb960a2Sdownsj  *
21467cb960a2Sdownsj  * RETURN VALUE:
21477cb960a2Sdownsj  *      cp or NULL
21487cb960a2Sdownsj  */
21497cb960a2Sdownsj 
21507cb960a2Sdownsj static char *
x_lastcp(void)2151c5d5393cSotto x_lastcp(void)
21527cb960a2Sdownsj {
21537894b443Smillert 	char *rcp;
21547894b443Smillert 	int i;
21557cb960a2Sdownsj 
21567894b443Smillert 	if (!xlp_valid) {
21577cb960a2Sdownsj 		for (i = 0, rcp = xbp; rcp < xep && i < x_displen; rcp++)
2158e569fc7cSderaadt 			i += x_size((unsigned char)*rcp);
21597cb960a2Sdownsj 		xlp = rcp;
21607cb960a2Sdownsj 	}
21610e7d3a01Smillert 	xlp_valid = true;
21627cb960a2Sdownsj 	return (xlp);
21637cb960a2Sdownsj }
21647cb960a2Sdownsj 
21659a36d1f0Santon #endif /* EMACS */
2166