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