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