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