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