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