1 /* $NetBSD: emacs.c,v 1.31 2006/05/13 21:58:51 christos Exp $ */
4 * Emacs-like command line editing and history
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
10 #include <sys/cdefs.h>
13 __RCSID("$NetBSD: emacs.c,v 1.31 2006/05/13 21:58:51 christos Exp $");
28 #define AEDIT &aedit /* area for kill ring and macro defns */
30 #undef CTRL /* _BSD brain damage */
31 #define CTRL(x) ((x) == '?' ? 0x7F : (x) & 0x1F) /* ASCII */
32 #define UNCTRL(x) ((x) == 0x7F ? '?' : (x) | 0x40) /* ASCII */
33 #define META(x) ((x) & 0x7f)
34 #define ISMETA(x) (Flag(FEMACSUSEMETA) && ((x) & 0x80))
37 /* values returned by keyboard functions */
39 #define KEOL 1 /* ^M, ^J */
40 #define KINTR 2 /* ^G, ^C */
43 int (*xf_func
) ARGS((int c
));
48 /* index into struct x_ftab x_ftab[] - small is good */
49 typedef unsigned char Findex
;
51 struct x_defbindings
{
52 Findex xdb_func
; /* XFUNC_* */
53 unsigned char xdb_tab
;
54 unsigned char xdb_char
;
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 */
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 */
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...)
69 # define CHARMASK 0xFF /* 8-bit ASCII character mask */
70 # define X_NTABS 4 /* normal, meta1, meta2, meta3 */
71 static int x_prefix3
= 0xE0;
73 # define CHARMASK 0xFF /* 8-bit character mask */
74 # define X_NTABS 3 /* normal, meta1, meta2 */
76 #define X_TABSZ (CHARMASK+1) /* size of keydef tables etc */
78 /* Arguments for do_complete()
79 * 0 = enumerate M-= complete as much as possible and then list
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) */
88 /* { from 4.9 edit.h */
90 * The following are used for my horizontal scrolling stuff
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 */
100 * we use x_adj_done so that functions can tell
101 * whether x_adjust() has been called while they are active.
103 static int x_adj_done
;
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 */
111 static int xlp_valid
;
112 /* end from 4.9 edit.h } */
114 static int x_prefix1
= CTRL('['), x_prefix2
= CTRL('X');
115 static char **x_histp
; /* history position */
116 static int x_nextcmd
; /* for newline-and-next */
117 static char *xmp
; /* mark pointer */
118 static Findex x_last_command
;
119 static Findex (*x_tab
)[X_TABSZ
]; /* key definition */
120 static char *(*x_atab
)[X_TABSZ
]; /* macro definitions */
121 static unsigned char x_bound
[(X_TABSZ
* X_NTABS
+ 7) / 8];
123 static char *killstack
[KILLSIZE
];
124 static int killsp
, killtp
;
125 static int x_curprefix
;
126 static char *macroptr
;
127 static int prompt_trunc
;
128 static int prompt_skip
;
130 static int x_ins
ARGS((char *cp
));
131 static void x_delete
ARGS((int nc
, int push
));
132 static int x_bword
ARGS((void));
133 static int x_fword
ARGS((void));
134 static void x_goto
ARGS((char *cp
));
135 static void x_bs
ARGS((int c
));
136 static int x_size_str
ARGS((char *cp
));
137 static int x_size
ARGS((int c
));
138 static void x_zots
ARGS((char *str
));
139 static void x_zotc
ARGS((int c
));
140 static void x_load_hist
ARGS((char **hp
));
141 static int x_search
ARGS((char *pat
, int sameline
, int offset
));
142 static int x_match
ARGS((char *str
, char *pat
));
143 static void x_redraw
ARGS((int limit
));
144 static void x_push
ARGS((int nchars
));
145 static char * x_mapin
ARGS((const char *cp
, Area
*area
));
146 static char * x_mapout
ARGS((int c
));
147 static void x_print
ARGS((int prefix
, int key
));
148 static void x_adjust
ARGS((void));
149 static void x_e_ungetc
ARGS((int c
));
150 static int x_e_getc
ARGS((void));
151 static void x_e_putc
ARGS((int c
));
152 static void x_e_puts
ARGS((const char *s
));
153 static int x_comment
ARGS((int c
));
154 static int x_fold_case
ARGS((int c
));
155 static char *x_lastcp
ARGS((void));
156 static void do_complete
ARGS((int flags
, Comp_type type
));
157 static int x_emacs_putbuf
ARGS((const char *s
, size_t len
));
160 /* The lines between START-FUNC-TAB .. END-FUNC-TAB are run through a
161 * script (emacs-gen.sh) that generates emacs.out which contains:
162 * - function declarations for x_* functions
163 * - defines of the form XFUNC_<name> where <name> is function
164 * name, sans leading x_.
165 * Note that the script treats #ifdef and { 0, 0, 0} specially - use with
169 static const struct x_ftab x_ftab
[] = {
170 /* @START-FUNC-TAB@ */
171 { x_abort
, "abort", 0 },
172 { x_beg_hist
, "beginning-of-history", 0 },
173 { x_comp_comm
, "complete-command", 0 },
174 { x_comp_file
, "complete-file", 0 },
175 { x_complete
, "complete", 0 },
176 { x_del_back
, "delete-char-backward", XF_ARG
},
177 { x_del_bword
, "delete-word-backward", XF_ARG
},
178 { x_del_char
, "delete-char-forward", XF_ARG
},
179 { x_del_fword
, "delete-word-forward", XF_ARG
},
180 { x_del_line
, "kill-line", 0 },
181 { x_draw_line
, "redraw", 0 },
182 { x_end_hist
, "end-of-history", 0 },
183 { x_end_of_text
, "eot", 0 },
184 { x_enumerate
, "list", 0 },
185 { x_eot_del
, "eot-or-delete", XF_ARG
},
186 { x_error
, "error", 0 },
187 { x_goto_hist
, "goto-history", XF_ARG
},
188 { x_ins_string
, "macro-string", XF_NOBIND
},
189 { x_insert
, "auto-insert", XF_ARG
},
190 { x_kill
, "kill-to-eol", XF_ARG
},
191 { x_kill_region
, "kill-region", 0 },
192 { x_list_comm
, "list-command", 0 },
193 { x_list_file
, "list-file", 0 },
194 { x_literal
, "quote", 0 },
195 { x_meta1
, "prefix-1", XF_PREFIX
},
196 { x_meta2
, "prefix-2", XF_PREFIX
},
197 { x_meta_yank
, "yank-pop", 0 },
198 { x_mv_back
, "backward-char", XF_ARG
},
199 { x_mv_begin
, "beginning-of-line", 0 },
200 { x_mv_bword
, "backward-word", XF_ARG
},
201 { x_mv_end
, "end-of-line", 0 },
202 { x_mv_forw
, "forward-char", XF_ARG
},
203 { x_mv_fword
, "forward-word", XF_ARG
},
204 { x_newline
, "newline", 0 },
205 { x_next_com
, "down-history", XF_ARG
},
206 { x_nl_next_com
, "newline-and-next", 0 },
207 { x_noop
, "no-op", 0 },
208 { x_prev_com
, "up-history", XF_ARG
},
209 { x_prev_histword
, "prev-hist-word", XF_ARG
},
210 { x_search_char_forw
, "search-character-forward", XF_ARG
},
211 { x_search_char_back
, "search-character-backward", XF_ARG
},
212 { x_search_hist
, "search-history", 0 },
213 { x_set_mark
, "set-mark-command", 0 },
214 { x_stuff
, "stuff", 0 },
215 { x_stuffreset
, "stuff-reset", 0 },
216 { x_transpose
, "transpose-chars", 0 },
217 { x_version
, "version", 0 },
218 { x_xchg_point_mark
, "exchange-point-and-mark", 0 },
219 { x_yank
, "yank", 0 },
220 { x_comp_list
, "complete-list", 0 },
221 { x_expand
, "expand-file", 0 },
222 { x_fold_capitalize
, "capitalize-word", XF_ARG
},
223 { x_fold_lower
, "downcase-word", XF_ARG
},
224 { x_fold_upper
, "upcase-word", XF_ARG
},
225 { x_set_arg
, "set-arg", XF_NOBIND
},
226 { x_comment
, "comment", 0 },
228 { x_game_of_life
, "play-game-of-life", 0 },
233 { x_debug_info
, "debug-info", 0 },
238 { x_meta3
, "prefix-3", XF_PREFIX
},
245 static struct x_defbindings
const x_defbindings
[] = {
246 { XFUNC_del_back
, 0, CTRL('?') },
247 { XFUNC_del_bword
, 1, CTRL('?') },
248 { XFUNC_eot_del
, 0, CTRL('D') },
249 { XFUNC_del_back
, 0, CTRL('H') },
250 { XFUNC_del_bword
, 1, CTRL('H') },
251 { XFUNC_del_bword
, 1, 'h' },
252 { XFUNC_mv_bword
, 1, 'b' },
253 { XFUNC_mv_fword
, 1, 'f' },
254 { XFUNC_del_fword
, 1, 'd' },
255 { XFUNC_mv_back
, 0, CTRL('B') },
256 { XFUNC_mv_forw
, 0, CTRL('F') },
257 { XFUNC_search_char_forw
, 0, CTRL(']') },
258 { XFUNC_search_char_back
, 1, CTRL(']') },
259 { XFUNC_newline
, 0, CTRL('M') },
260 { XFUNC_newline
, 0, CTRL('J') },
261 { XFUNC_end_of_text
, 0, CTRL('_') },
262 { XFUNC_abort
, 0, CTRL('G') },
263 { XFUNC_prev_com
, 0, CTRL('P') },
264 { XFUNC_next_com
, 0, CTRL('N') },
265 { XFUNC_nl_next_com
, 0, CTRL('O') },
266 { XFUNC_search_hist
, 0, CTRL('R') },
267 { XFUNC_beg_hist
, 1, '<' },
268 { XFUNC_end_hist
, 1, '>' },
269 { XFUNC_goto_hist
, 1, 'g' },
270 { XFUNC_mv_end
, 0, CTRL('E') },
271 { XFUNC_mv_begin
, 0, CTRL('A') },
272 { XFUNC_draw_line
, 0, CTRL('L') },
273 { XFUNC_meta1
, 0, CTRL('[') },
274 { XFUNC_meta2
, 0, CTRL('X') },
275 { XFUNC_kill
, 0, CTRL('K') },
276 { XFUNC_yank
, 0, CTRL('Y') },
277 { XFUNC_meta_yank
, 1, 'y' },
278 { XFUNC_literal
, 0, CTRL('^') },
279 { XFUNC_comment
, 1, '#' },
280 #if defined(BRL) && defined(TIOCSTI)
281 { XFUNC_stuff
, 0, CTRL('T') },
283 { XFUNC_transpose
, 0, CTRL('T') },
285 { XFUNC_complete
, 1, CTRL('[') },
286 { XFUNC_comp_list
, 0, CTRL('I') },
287 { XFUNC_comp_list
, 1, '=' },
288 { XFUNC_enumerate
, 1, '?' },
289 { XFUNC_expand
, 1, '*' },
290 { XFUNC_comp_file
, 1, CTRL('X') },
291 { XFUNC_comp_comm
, 2, CTRL('[') },
292 { XFUNC_list_comm
, 2, '?' },
293 { XFUNC_list_file
, 2, CTRL('Y') },
294 { XFUNC_set_mark
, 1, ' ' },
295 { XFUNC_kill_region
, 0, CTRL('W') },
296 { XFUNC_xchg_point_mark
, 2, CTRL('X') },
297 { XFUNC_version
, 0, CTRL('V') },
299 { XFUNC_debug_info
, 1, CTRL('H') },
301 { XFUNC_prev_histword
, 1, '.' },
302 { XFUNC_prev_histword
, 1, '_' },
303 { XFUNC_set_arg
, 1, '0' },
304 { XFUNC_set_arg
, 1, '1' },
305 { XFUNC_set_arg
, 1, '2' },
306 { XFUNC_set_arg
, 1, '3' },
307 { XFUNC_set_arg
, 1, '4' },
308 { XFUNC_set_arg
, 1, '5' },
309 { XFUNC_set_arg
, 1, '6' },
310 { XFUNC_set_arg
, 1, '7' },
311 { XFUNC_set_arg
, 1, '8' },
312 { XFUNC_set_arg
, 1, '9' },
313 { XFUNC_fold_upper
, 1, 'U' },
314 { XFUNC_fold_upper
, 1, 'u' },
315 { XFUNC_fold_lower
, 1, 'L' },
316 { XFUNC_fold_lower
, 1, 'l' },
317 { XFUNC_fold_capitalize
, 1, 'C' },
318 { XFUNC_fold_capitalize
, 1, 'c' },
320 { XFUNC_meta3
, 0, 0xE0 },
321 { XFUNC_mv_back
, 3, 'K' },
322 { XFUNC_mv_forw
, 3, 'M' },
323 { XFUNC_next_com
, 3, 'P' },
324 { XFUNC_prev_com
, 3, 'H' },
326 /* These for ansi arrow keys: arguablely shouldn't be here by
327 * default, but its simpler/faster/smaller than using termcap
330 { XFUNC_meta2
, 1, '[' },
331 { XFUNC_meta2
, 1, 'O' },
332 { XFUNC_prev_com
, 2, 'A' },
333 { XFUNC_next_com
, 2, 'B' },
334 { XFUNC_mv_forw
, 2, 'C' },
335 { XFUNC_mv_back
, 2, 'D' },
348 xbp
= xbuf
= buf
; xend
= buf
+ len
;
349 xlp
= xcp
= xep
= buf
;
354 macroptr
= (char *) 0;
355 x_histp
= histptr
+ 1;
356 x_last_command
= XFUNC_error
;
359 x_col
= promptlen(prompt
, &p
);
360 prompt_skip
= p
- prompt
;
361 prompt_trunc
= x_col
- (x_cols
- 3 - MIN_EDIT_SPACE
);
362 if (prompt_trunc
> 0)
363 x_col
-= prompt_trunc
;
367 x_displen
= xx_cols
- 2 - x_col
;
370 pprompt(prompt
, prompt_trunc
);
372 if (x_nextcmd
>= 0) {
373 int off
= source
->line
- x_nextcmd
;
374 if (histptr
- histlist
>= off
)
375 x_load_hist(histptr
- off
);
381 if ((c
= x_e_getc()) < 0)
389 f
= x_curprefix
== -1 ? XFUNC_insert
390 : x_tab
[x_curprefix
][c
&CHARMASK
];
392 if (!(x_ftab
[f
].xf_flags
& XF_PREFIX
)
393 && x_last_command
!= XFUNC_set_arg
)
398 i
= c
| (x_curprefix
<< 8);
400 switch (i
= (*x_ftab
[f
].xf_func
)(i
)) {
402 if (!(x_ftab
[f
].xf_flags
& XF_PREFIX
))
408 case KINTR
: /* special case for interrupt */
423 * Should allow tab and control chars.
444 macroptr
= x_atab
[c
>>8][c
& CHARMASK
];
445 if (macroptr
&& !*macroptr
) {
447 macroptr
= (char *) 0;
452 static int x_do_ins(const char *cp
, int len
);
459 if (xep
+len
>= xend
) {
464 memmove(xcp
+len
, xcp
, xep
- xcp
+ 1);
465 memmove(xcp
, cp
, len
);
476 register int adj
= x_adj_done
;
478 if (x_do_ins(s
, strlen(s
)) < 0)
481 * x_zots() may result in a call to x_adjust()
482 * we want xcp to reflect the new position.
486 x_adj_ok
= (xcp
>= xlp
);
488 if (adj
== x_adj_done
) /* has x_adjust() been called? */
491 for (cp
= xlp
; cp
> xcp
; )
500 * this is used for x_escape() in do_complete()
503 x_emacs_putbuf(s
, len
)
509 if ((rval
= x_do_ins(s
, len
)) != 0)
518 int col
= xcp
- xbuf
;
527 x_delete(x_arg
, FALSE
);
535 int nleft
= xep
- xcp
;
543 x_delete(x_arg
, FALSE
);
547 /* Delete nc chars to the right of the cursor (including cursor position) */
558 if (xmp
!= NULL
&& xmp
> xcp
) {
566 * This lets us yank a word we have deleted.
578 memmove(xcp
, xcp
+nc
, xep
- xcp
+ 1); /* Copies the null */
579 x_adj_ok
= 0; /* don't redraw */
582 * if we are already filling the line,
583 * there is no need to ' ','\b'.
584 * But if we must, make sure we do the minimum.
586 if ((i
= x_displen
) > 0)
599 for (cp
= x_lastcp(); cp
> xcp
; )
609 x_delete(x_bword(), TRUE
);
625 x_goto(xcp
+ x_fword());
633 x_delete(x_fword(), TRUE
);
641 register char *cp
= xcp
;
649 while (cp
!= xbuf
&& is_mfs(cp
[-1]))
654 while (cp
!= xbuf
&& !is_mfs(cp
[-1]))
668 register char *cp
= xcp
;
676 while (cp
!= xep
&& is_mfs(*cp
))
681 while (cp
!= xep
&& !is_mfs(*cp
))
694 if (cp
< xbp
|| cp
>= (xbp
+ x_displen
))
696 /* we are heading off screen */
702 if (cp
< xcp
) /* move back */
709 if (cp
> xcp
) /* move forward */
732 register int size
= 0;
734 size
+= x_size(*cp
++);
743 return 4; /* Kludge, tabs are always four spaces. */
744 if (iscntrl((unsigned char)c
)) /* control char */
753 register int adj
= x_adj_done
;
756 while (*str
&& str
< xlp
&& adj
== x_adj_done
)
765 /* Kludge, tabs are always four spaces. */
767 } else if (iscntrl((unsigned char)c
)) {
778 int col
= xcp
- xbuf
;
794 int nleft
= xep
- xcp
;
807 x_search_char_forw(c
)
816 || ((cp
= (cp
== xep
) ? NULL
: strchr(cp
+ 1, c
)) == NULL
817 && (cp
= strchr(xbuf
, c
)) == NULL
))
828 x_search_char_back(c
)
834 for (; x_arg
--; cp
= p
)
838 if (c
< 0 || p
== cp
) {
867 static int x_beg_hist(c
) int c
; { x_load_hist(histlist
); return KSTD
;}
869 static int x_end_hist(c
) int c
; { x_load_hist(histptr
); return KSTD
;}
871 static int x_prev_com(c
) int c
; { x_load_hist(x_histp
- x_arg
); return KSTD
;}
873 static int x_next_com(c
) int c
; { x_load_hist(x_histp
+ x_arg
); return KSTD
;}
875 /* Goto a particular history number obtained from argument.
876 * If no argument is given history 1 is probably not what you
877 * want so we'll simply go to the oldest one.
884 x_load_hist(histlist
);
886 x_load_hist(histptr
+ x_arg
- source
->line
);
896 if (hp
< histlist
|| hp
> histptr
) {
901 oldsize
= x_size_str(xbuf
);
902 strlcpy(xbuf
, *hp
, xend
- xbuf
);
904 xep
= xcp
= xbuf
+ strlen(xbuf
);
906 if (xep
> x_lastcp())
916 x_nextcmd
= source
->line
- (histptr
- x_histp
) + 1;
917 return (x_newline(c
));
924 if (xep
== xbuf
&& x_arg_defaulted
)
925 return (x_end_of_text(c
));
927 return (x_del_char(c
));
930 /* reverse incremental history search */
935 int offset
= -1; /* offset of match in xbuf, else -1 */
936 char pat
[256+1]; /* pattern buffer */
937 register char *p
= pat
;
943 x_e_puts("\nI-search: ");
947 if ((c
= x_e_getc()) < 0)
949 f
= x_tab
[0][c
&CHARMASK
];
952 else if (f
== XFUNC_search_hist
)
953 offset
= x_search(pat
, 0, offset
);
954 else if (f
== XFUNC_del_back
) {
964 offset
= x_search(pat
, 1, offset
);
966 } else if (f
== XFUNC_insert
) {
967 /* add char to pattern */
968 /* overflow check... */
969 if (p
>= &pat
[sizeof(pat
) - 1]) {
975 /* already have partial match */
976 offset
= x_match(xbuf
, pat
);
978 x_goto(xbuf
+ offset
+ (p
- pat
) - (*pat
== '^'));
982 offset
= x_search(pat
, 0, offset
);
983 } else { /* other command */
993 /* search backward from current line */
995 x_search(pat
, sameline
, offset
)
1003 for (hp
= x_histp
- (sameline
? 0 : 1) ; hp
>= histlist
; --hp
) {
1004 i
= x_match(*hp
, pat
);
1009 x_goto(xbuf
+ i
+ strlen(pat
) - (*pat
== '^'));
1018 /* return position of first match of pattern in string, else -1 */
1024 return (strncmp(str
, pat
+1, strlen(pat
+1)) == 0) ? 0 : -1;
1026 char *q
= strstr(str
, pat
);
1027 return (q
== NULL
) ? -1 : q
- str
;
1039 j
= x_size_str(xbuf
);
1042 xlp
= xbp
= xep
= xbuf
;
1075 /* Redraw (part of) the line. If limit is < 0, the everything is redrawn
1076 * on a NEW line, otherwise limit is the screen column up to which needs
1094 pprompt(prompt
+ prompt_skip
, 0);
1095 x_col
= promptlen(prompt
, (const char **) 0);
1097 x_displen
= xx_cols
- 2 - x_col
;
1101 if (xbp
!= xbuf
|| xep
> xlp
)
1106 i
= 0; /* we fill the line */
1108 i
= limit
- (xlp
- xbp
);
1110 for (j
= 0; j
< i
&& x_col
< (xx_cols
- 2); j
++)
1113 if (xep
> xlp
) /* more off screen */
1128 for (cp
= xlp
; cp
> xcp
; )
1141 /* What transpose is meant to do seems to be up for debate. This
1142 * is a general summary of the options; the text is abcd with the
1143 * upper case character or underscore indicating the cursor position:
1144 * Who Before After Before After
1145 * at&t ksh in emacs mode: abCd abdC abcd_ (bell)
1146 * at&t ksh in gmacs mode: abCd baCd abcd_ abdc_
1147 * gnu emacs: abCd acbD abcd_ abdc_
1148 * Pdksh currently goes with GNU behavior since I believe this is the
1149 * most common version of emacs, unless in gmacs mode, in which case
1150 * it does the at&t ksh gmacs mode.
1151 * This should really be broken up into 3 functions so users can bind
1152 * to the one they want.
1157 } else if (xcp
== xep
|| Flag(FGMACS
)) {
1158 if (xcp
- xbuf
== 1) {
1162 /* Gosling/Unipress emacs style: Swap two characters before the
1163 * cursor, do not change cursor position
1173 /* GNU emacs style: Swap the characters before and under the
1174 * cursor, move cursor position along one.
1226 int col
= xcp
- xbuf
;
1227 int lastcol
= xep
- xbuf
;
1230 if (x_arg_defaulted
)
1232 else if (x_arg
> lastcol
)
1236 x_goto(xbuf
+ x_arg
);
1239 x_delete(ndel
, TRUE
);
1247 char *cp
= str_nsave(xcp
, nchars
, AEDIT
);
1248 if (killstack
[killsp
])
1249 afree((void *)killstack
[killsp
], AEDIT
);
1250 killstack
[killsp
] = cp
;
1251 killsp
= (killsp
+ 1) % KILLSIZE
;
1263 if (killstack
[killtp
] == 0) {
1264 x_e_puts("\nnothing to yank");
1269 x_ins(killstack
[killtp
]);
1278 if ((x_last_command
!= XFUNC_yank
&& x_last_command
!= XFUNC_meta_yank
)
1279 || killstack
[killtp
] == 0) {
1281 x_e_puts("\nyank something first");
1285 len
= strlen(killstack
[killtp
]);
1287 x_delete(len
, FALSE
);
1290 killtp
= KILLSIZE
- 1;
1293 } while (killstack
[killtp
] == 0);
1294 x_ins(killstack
[killtp
]);
1303 xlp
= xep
= xcp
= xbp
= xbuf
;
1326 xlp
= xcp
= xep
= xbp
= xbuf
;
1338 #if 0 || defined TIOCSTI
1340 bool_t savmode
= x_mode(FALSE
);
1342 (void)ioctl(TTY
, TIOCSTI
, &ch
);
1343 (void)x_mode(savmode
);
1356 op
= new = str_save(cp
, area
);
1358 /* XXX -- should handle \^ escape? */
1362 if (*cp
== '0') /* To define function keys */
1366 if (*cp
>= '?') /* includes '?'; ASCII */
1386 register char *p
= buf
;
1394 if (iscntrl((unsigned char)c
)) {
1404 x_print(prefix
, key
)
1408 shprintf("%s", x_mapout(x_prefix1
));
1410 shprintf("%s", x_mapout(x_prefix2
));
1413 shprintf("%s", x_mapout(x_prefix3
));
1415 shprintf("%s = ", x_mapout(key
));
1416 if (x_tab
[prefix
][key
] != XFUNC_ins_string
)
1417 shprintf("%s\n", x_ftab
[x_tab
[prefix
][key
]].xf_name
);
1419 shprintf("'%s'\n", x_atab
[prefix
][key
]);
1423 x_bind(a1
, a2
, macro
, list
)
1424 const char *a1
, *a2
;
1425 int macro
; /* bind -m */
1426 int list
; /* bind -l */
1433 if (x_tab
== NULL
) {
1434 bi_errorf("cannot bind, not a tty");
1438 /* List function names */
1440 for (f
= 0; f
< NELEM(x_ftab
); f
++)
1441 if (x_ftab
[f
].xf_name
1442 && !(x_ftab
[f
].xf_flags
& XF_NOBIND
))
1443 shprintf("%s\n", x_ftab
[f
].xf_name
);
1448 for (prefix
= 0; prefix
< X_NTABS
; prefix
++)
1449 for (key
= 0; key
< X_TABSZ
; key
++) {
1450 f
= x_tab
[prefix
][key
];
1451 if (f
== XFUNC_insert
|| f
== XFUNC_error
1452 || (macro
&& f
!= XFUNC_ins_string
))
1454 x_print(prefix
, key
);
1459 m2
= m1
= x_mapin(a1
, ATEMP
);
1462 key
= *m1
& CHARMASK
;
1463 if (x_tab
[prefix
][key
] == XFUNC_meta1
)
1465 else if (x_tab
[prefix
][key
] == XFUNC_meta2
)
1468 else if (x_tab
[prefix
][key
] == XFUNC_meta3
)
1477 x_print(prefix
, key
);
1484 for (f
= 0; f
< NELEM(x_ftab
); f
++)
1485 if (x_ftab
[f
].xf_name
1486 && strcmp(x_ftab
[f
].xf_name
, a2
) == 0)
1488 if (f
== NELEM(x_ftab
) || x_ftab
[f
].xf_flags
& XF_NOBIND
) {
1489 bi_errorf("%s: no such function", a2
);
1492 #if 0 /* This breaks the bind commands that map arrow keys */
1493 if (f
== XFUNC_meta1
)
1495 if (f
== XFUNC_meta2
)
1499 f
= XFUNC_ins_string
;
1500 sp
= x_mapin(a2
, AEDIT
);
1503 if (x_tab
[prefix
][key
] == XFUNC_ins_string
&& x_atab
[prefix
][key
])
1504 afree((void *)x_atab
[prefix
][key
], AEDIT
);
1505 x_tab
[prefix
][key
] = f
;
1506 x_atab
[prefix
][key
] = sp
;
1508 /* Track what the user has bound so x_emacs_keys() won't toast things */
1509 if (f
== XFUNC_insert
)
1510 x_bound
[(prefix
* X_TABSZ
+ key
) / 8] &=
1511 ~(1 << ((prefix
* X_TABSZ
+ key
) % 8));
1513 x_bound
[(prefix
* X_TABSZ
+ key
) / 8] |=
1514 (1 << ((prefix
* X_TABSZ
+ key
) % 8));
1529 x_tab
= (Findex (*)[X_TABSZ
]) alloc(sizeofN(*x_tab
, X_NTABS
), AEDIT
);
1530 for (j
= 0; j
< X_TABSZ
; j
++)
1531 x_tab
[0][j
] = XFUNC_insert
;
1532 for (i
= 1; i
< X_NTABS
; i
++)
1533 for (j
= 0; j
< X_TABSZ
; j
++)
1534 x_tab
[i
][j
] = XFUNC_error
;
1535 for (i
= 0; i
< NELEM(x_defbindings
); i
++)
1536 x_tab
[(unsigned char)x_defbindings
[i
].xdb_tab
][x_defbindings
[i
].xdb_char
]
1537 = x_defbindings
[i
].xdb_func
;
1539 x_atab
= (char *(*)[X_TABSZ
]) alloc(sizeofN(*x_atab
, X_NTABS
), AEDIT
);
1540 for (i
= 1; i
< X_NTABS
; i
++)
1541 for (j
= 0; j
< X_TABSZ
; j
++)
1542 x_atab
[i
][j
] = NULL
;
1544 /* Determine if we can translate meta key or use 8-bit AscII
1545 * XXX - It would be nice if there was a locale attribute to
1546 * determine if the locale is 7-bit or not.
1548 locale
= setlocale(LC_CTYPE
, NULL
);
1549 if (locale
== NULL
|| !strcmp(locale
, "C") || !strcmp(locale
, "POSIX"))
1550 Flag(FEMACSUSEMETA
) = 0;
1553 static void bind_if_not_bound(int p
, int k
, int func
);
1556 bind_if_not_bound(p
, k
, func
)
1560 /* Has user already bound this key? If so, don't override it */
1561 if (x_bound
[((p
) * X_TABSZ
+ (k
)) / 8]
1562 & (1 << (((p
) * X_TABSZ
+ (k
)) % 8)))
1572 if (ec
->erase
>= 0) {
1573 bind_if_not_bound(0, ec
->erase
, XFUNC_del_back
);
1574 bind_if_not_bound(1, ec
->erase
, XFUNC_del_bword
);
1577 bind_if_not_bound(0, ec
->kill
, XFUNC_del_line
);
1578 if (ec
->werase
>= 0)
1579 bind_if_not_bound(0, ec
->werase
, XFUNC_del_bword
);
1581 bind_if_not_bound(0, ec
->intr
, XFUNC_abort
);
1583 bind_if_not_bound(0, ec
->quit
, XFUNC_noop
);
1613 x_delete(rsize
, TRUE
);
1619 x_xchg_point_mark(c
)
1638 char *o_xbuf
= xbuf
, *o_xend
= xend
;
1639 char *o_xbp
= xbp
, *o_xep
= xep
, *o_xcp
= xcp
;
1640 int lim
= x_lastcp() - xbp
;
1642 xbuf
= xbp
= xcp
= ksh_version
+ 4;
1643 xend
= xep
= ksh_version
+ 4 + strlen(ksh_version
+ 4);
1653 x_redraw(strlen(ksh_version
));
1657 /* This is what at&t ksh seems to do... Very bizarre */
1676 char newbuf
[256+1];
1677 register char *ip
, *op
;
1682 len
= x_size_str(xbuf
);
1684 memmove(newbuf
+1, xbuf
, i
);
1687 for (ip
= newbuf
+1, op
= xbuf
; --i
>= 0; ip
++, op
++) {
1689 if (*ip
< '@' || *ip
== '_' || *ip
== 0x7F) {
1690 /* Two adults, make whoopee */
1691 if (ip
[-1] < '_' && ip
[1] < '_') {
1692 /* Make kid look like parents. */
1693 *op
= '`' + ((ip
[-1] + ip
[1])/2)%32;
1694 if (*op
== 0x7F) /* Birth defect */
1698 *op
= ' '; /* nothing happens */
1703 /* All alone, dies */
1704 if (ip
[-1] == ' ' && ip
[1] == ' ')
1706 else /* Gets older */
1711 /* Overcrowded, dies */
1712 if (ip
[-1] >= '@' && ip
[1] >= '@') {
1725 * File/command name completion routines
1733 do_complete(XCF_COMMAND
, CT_COMPLETE
);
1740 do_complete(XCF_COMMAND
, CT_LIST
);
1747 do_complete(XCF_COMMAND_FILE
, CT_COMPLETE
);
1754 do_complete(XCF_COMMAND_FILE
, CT_LIST
);
1761 do_complete(XCF_FILE
, CT_COMPLETE
);
1768 do_complete(XCF_FILE
, CT_LIST
);
1775 do_complete(XCF_COMMAND_FILE
, CT_COMPLIST
);
1788 nwords
= x_cf_glob(XCF_FILE
,
1789 xbuf
, xep
- xbuf
, xcp
- xbuf
,
1790 &start
, &end
, &words
, &is_command
);
1797 x_goto(xbuf
+ start
);
1798 x_delete(end
- start
, FALSE
);
1799 for (i
= 0; i
< nwords
;) {
1800 if (x_escape(words
[i
], strlen(words
[i
]), x_emacs_putbuf
) < 0 ||
1801 (++i
< nwords
&& x_ins(space
) < 0))
1812 /* type == 0 for list, 1 for complete and 2 for complete-list */
1814 do_complete(flags
, type
)
1815 int flags
; /* XCF_{COMMAND,FILE,COMMAND_FILE} */
1820 int start
, end
, nlen
, olen
;
1824 nwords
= x_cf_glob(flags
, xbuf
, xep
- xbuf
, xcp
- xbuf
,
1825 &start
, &end
, &words
, &is_command
);
1832 if (type
== CT_LIST
) {
1833 x_print_expansions(nwords
, words
, is_command
);
1835 x_free_words(nwords
, words
);
1840 nlen
= x_longest_prefix(nwords
, words
);
1842 if (nwords
== 1 || nlen
> olen
) {
1843 x_goto(xbuf
+ start
);
1844 x_delete(olen
, FALSE
);
1845 x_escape(words
[0], nlen
, x_emacs_putbuf
);
1849 /* add space if single non-dir match */
1850 if ((nwords
== 1) && (!ISDIRSEP(words
[0][nlen
- 1]))) {
1855 if (type
== CT_COMPLIST
&& !completed
) {
1856 x_print_expansions(nwords
, words
, is_command
);
1863 x_free_words(nwords
, words
);
1867 * x_adjust - redraw the line adjusting starting point etc.
1870 * This function is called when we have exceeded the bounds
1871 * of the edit window. It increments x_adj_done so that
1872 * functions like x_ins and x_delete know that we have been
1873 * called and can skip the x_bs() stuff which has already
1874 * been done by x_redraw.
1883 x_adj_done
++; /* flag the fact that we were called. */
1885 * we had a problem if the prompt length > xx_cols / 2
1887 if ((xbp
= xcp
- (x_displen
/ 2)) < xbuf
)
1894 static int unget_char
= -1;
1908 if (unget_char
>= 0) {
1913 c
= (unsigned char) *macroptr
++;
1915 macroptr
= (char *) 0;
1920 return c
<= CHARMASK
? c
: (c
& CHARMASK
);
1927 if (c
== '\r' || c
== '\n')
1929 if (x_col
< xx_cols
)
1947 if (x_adj_ok
&& (x_col
< 0 || x_col
>= (xx_cols
- 2)))
1959 shellf("\nksh debug:\n");
1960 shellf("\tx_col == %d,\t\tx_cols == %d,\tx_displen == %d\n",
1961 x_col
, xx_cols
, x_displen
);
1962 shellf("\txcp == 0x%lx,\txep == 0x%lx\n", (long) xcp
, (long) xep
);
1963 shellf("\txbp == 0x%lx,\txbuf == 0x%lx\n", (long) xbp
, (long) xbuf
);
1964 shellf("\txlp == 0x%lx\n", (long) xlp
);
1965 shellf("\txlp == 0x%lx\n", (long) x_lastcp());
1976 register int adj
= x_adj_done
;
1978 while (*s
&& adj
== x_adj_done
)
1983 * x_set_arg - set an arg value for next function
1986 * This is a simple implementation of M-[0-9].
1999 c
&= CHARMASK
; /* strip command prefix */
2000 for (; c
>= 0 && isdigit(c
); c
= x_e_getc(), first
= 0)
2001 n
= n
* 10 + (c
- '0');
2002 if (c
< 0 || first
) {
2005 x_arg_defaulted
= 1;
2009 x_arg_defaulted
= 0;
2015 /* Comment or uncomment the current line. */
2020 int oldsize
= x_size_str(xbuf
);
2021 int len
= xep
- xbuf
;
2022 int ret
= x_do_comment(xbuf
, xend
- xbuf
, &len
);
2032 return x_newline('\n');
2039 * x_prev_histword - recover word from prev command
2042 * This function recovers the last word from the previous
2043 * command and inserts it into the current edit line. If a
2044 * numeric arg is supplied then the n'th word from the
2045 * start of the previous command is used.
2063 else if (x_arg_defaulted
) {
2064 rcp
= &cp
[strlen(cp
) - 1];
2066 * ignore white-space after the last word
2068 while (rcp
> cp
&& is_cfs(*rcp
))
2070 while (rcp
> cp
&& !is_cfs(*rcp
))
2080 * ignore white-space at start of line
2082 while (*rcp
&& is_cfs(*rcp
))
2086 while (*rcp
&& !is_cfs(*rcp
))
2088 while (*rcp
&& is_cfs(*rcp
))
2092 while (*rcp
&& !is_cfs(*rcp
))
2102 /* Uppercase N(1) words */
2107 return x_fold_case('U');
2110 /* Lowercase N(1) words */
2115 return x_fold_case('L');
2118 /* Lowercase N(1) words */
2120 x_fold_capitalize(c
)
2123 return x_fold_case('C');
2127 * x_fold_case - convert word to UPPER/lower/Capital case
2130 * This function is used to implement M-U,M-u,M-L,M-l,M-C and M-c
2131 * to UPPER case, lower case or Capitalize words.
2149 * first skip over any white-space
2151 while (cp
!= xep
&& is_mfs(*cp
))
2154 * do the first char on its own since it may be
2155 * a different action than for the rest.
2158 if (c
== 'L') { /* lowercase */
2159 if (isupper((unsigned char)*cp
))
2160 *cp
= tolower((unsigned char)*cp
);
2161 } else { /* uppercase, capitialize */
2162 if (islower((unsigned char)*cp
))
2163 *cp
= toupper((unsigned char)*cp
);
2168 * now for the rest of the word
2170 while (cp
!= xep
&& !is_mfs((unsigned char)*cp
)) {
2171 if (c
== 'U') { /* uppercase */
2172 if (islower((unsigned char)*cp
))
2173 *cp
= toupper((unsigned char)*cp
);
2174 } else { /* lowercase, capitialize */
2175 if (isupper((unsigned char)*cp
))
2176 *cp
= tolower((unsigned char)*cp
);
2186 * x_lastcp - last visible char
2192 * This function returns a pointer to that char in the
2193 * edit buffer that will be the last displayed on the
2194 * screen. The sequence:
2196 * for (cp = x_lastcp(); cp > xcp; cp)
2199 * Will position the cursor correctly on the screen.
2213 for (i
= 0, rcp
= xbp
; rcp
< xep
&& i
< x_displen
; rcp
++)