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