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