1 /* Yash: yet another shell */
2 /* keymap.c: mappings from keys to functions */
3 /* (C) 2007-2018 magicant */
4
5 /* This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>. */
17
18
19 #include "../common.h"
20 #include "keymap.h"
21 #include <assert.h>
22 #include <errno.h>
23 #if HAVE_GETTEXT
24 # include <libintl.h>
25 #endif
26 #include <stdbool.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include "../builtin.h"
30 #include "../exec.h"
31 #include "../expand.h"
32 #include "../util.h"
33 #include "../xfnmatch.h"
34 #include "complete.h"
35 #include "editing.h"
36 #include "key.h"
37 #include "trie.h"
38
39
40 /* Definition of editing modes. */
41 le_mode_T le_modes[LE_MODE_N];
42
43 /* The current editing mode.
44 * Points to one of the modes in `le_modes'. */
45 le_mode_T *le_current_mode;
46
47 /* Array of pairs of a command name and function.
48 * Sorted by name. */
49 static const struct command_name_pair {
50 const char *name;
51 le_command_func_T *command;
52 } commands[] = {
53 #include "commands.in"
54 };
55
56
57 /* Initializes `le_modes' if not yet initialized.
58 * May be called more than once but does nothing if so. */
le_keymap_init(void)59 void le_keymap_init(void)
60 {
61 static bool initialized = false;
62 if (initialized)
63 return;
64 initialized = true;
65
66 trie_T *t;
67
68 #define Set(key, cmd) \
69 (t = trie_setw(t, key, (trievalue_T) { .cmdfunc = (cmd) }))
70
71 le_modes[LE_MODE_VI_INSERT].default_command = cmd_self_insert;
72 t = trie_create();
73 Set(Key_backslash, cmd_self_insert);
74 Set(Key_c_v, cmd_expect_verbatim);
75 Set(Key_right, cmd_forward_char);
76 Set(Key_left, cmd_backward_char);
77 Set(Key_home, cmd_beginning_of_line);
78 Set(Key_end, cmd_end_of_line);
79 Set(Key_c_j, cmd_accept_line);
80 Set(Key_c_m, cmd_accept_line);
81 Set(Key_interrupt, cmd_abort_line);
82 Set(Key_c_c, cmd_abort_line);
83 Set(Key_eof, cmd_eof_if_empty);
84 Set(Key_c_d, cmd_eof_if_empty);
85 Set(Key_escape, cmd_setmode_vicommand);
86 Set(Key_c_l, cmd_redraw_all);
87 Set(Key_delete, cmd_delete_char);
88 Set(Key_backspace, cmd_backward_delete_char);
89 Set(Key_erase, cmd_backward_delete_char);
90 Set(Key_c_h, cmd_backward_delete_char);
91 Set(Key_c_w, cmd_backward_delete_semiword);
92 Set(Key_kill, cmd_backward_delete_line);
93 Set(Key_c_u, cmd_backward_delete_line);
94 Set(Key_tab, cmd_complete_next_candidate);
95 Set(Key_btab, cmd_complete_prev_candidate);
96 Set(Key_down, cmd_next_history_eol);
97 Set(Key_c_n, cmd_next_history_eol);
98 Set(Key_up, cmd_prev_history_eol);
99 Set(Key_c_p, cmd_prev_history_eol);
100 le_modes[LE_MODE_VI_INSERT].keymap = t;
101
102 le_modes[LE_MODE_VI_COMMAND].default_command = cmd_alert;
103 t = trie_create();
104 Set(Key_escape, cmd_noop);
105 Set(L"1", cmd_digit_argument);
106 Set(L"2", cmd_digit_argument);
107 Set(L"3", cmd_digit_argument);
108 Set(L"4", cmd_digit_argument);
109 Set(L"5", cmd_digit_argument);
110 Set(L"6", cmd_digit_argument);
111 Set(L"7", cmd_digit_argument);
112 Set(L"8", cmd_digit_argument);
113 Set(L"9", cmd_digit_argument);
114 Set(L"0", cmd_bol_or_digit);
115 Set(Key_c_j, cmd_accept_line);
116 Set(Key_c_m, cmd_accept_line);
117 Set(Key_interrupt, cmd_abort_line);
118 Set(Key_c_c, cmd_abort_line);
119 Set(Key_eof, cmd_eof_if_empty);
120 Set(Key_c_d, cmd_eof_if_empty);
121 Set(L"#", cmd_accept_with_hash);
122 Set(L"i", cmd_setmode_viinsert);
123 Set(Key_insert, cmd_setmode_viinsert);
124 Set(Key_c_l, cmd_redraw_all);
125 Set(L"l", cmd_forward_char);
126 Set(L" ", cmd_forward_char);
127 Set(Key_right, cmd_forward_char);
128 Set(L"h", cmd_backward_char);
129 Set(Key_left, cmd_backward_char);
130 Set(Key_backspace, cmd_backward_char);
131 Set(Key_erase, cmd_backward_char);
132 Set(Key_c_h, cmd_backward_char);
133 Set(L"W", cmd_forward_bigword);
134 Set(L"E", cmd_end_of_bigword);
135 Set(L"B", cmd_backward_bigword);
136 Set(L"w", cmd_forward_viword);
137 Set(L"e", cmd_end_of_viword);
138 Set(L"b", cmd_backward_viword);
139 Set(Key_home, cmd_beginning_of_line);
140 Set(L"$", cmd_end_of_line);
141 Set(Key_end, cmd_end_of_line);
142 Set(L"^", cmd_first_nonblank);
143 Set(L"f", cmd_find_char);
144 Set(L"F", cmd_find_char_rev);
145 Set(L"t", cmd_till_char);
146 Set(L"T", cmd_till_char_rev);
147 Set(L";", cmd_refind_char);
148 Set(L",", cmd_refind_char_rev);
149 Set(L"x", cmd_kill_char);
150 Set(Key_delete, cmd_kill_char);
151 Set(L"X", cmd_backward_kill_char);
152 Set(L"P", cmd_put_before);
153 Set(L"p", cmd_put);
154 Set(L"u", cmd_undo);
155 Set(L"U", cmd_undo_all);
156 Set(Key_c_r, cmd_cancel_undo);
157 Set(L".", cmd_redo);
158 Set(L"|", cmd_go_to_column);
159 Set(L"r", cmd_vi_replace_char);
160 Set(L"I", cmd_vi_insert_beginning);
161 Set(L"a", cmd_vi_append);
162 Set(L"A", cmd_vi_append_to_eol);
163 Set(L"R", cmd_vi_replace);
164 Set(L"~", cmd_vi_switch_case_char);
165 Set(L"y", cmd_vi_yank);
166 Set(L"Y", cmd_vi_yank_to_eol);
167 Set(L"d", cmd_vi_delete);
168 Set(L"D", cmd_vi_delete_to_eol);
169 Set(L"c", cmd_vi_change);
170 Set(L"C", cmd_vi_change_to_eol);
171 Set(L"S", cmd_vi_change_line);
172 Set(L"s", cmd_vi_substitute);
173 Set(L"_", cmd_vi_append_last_bigword);
174 Set(L"@", cmd_vi_exec_alias);
175 Set(L"v", cmd_vi_edit_and_accept);
176 Set(L"=", cmd_vi_complete_list);
177 Set(L"*", cmd_vi_complete_all);
178 Set(Key_backslash, cmd_vi_complete_max);
179 Set(L"?", cmd_vi_search_forward);
180 Set(L"/", cmd_vi_search_backward);
181 Set(L"n", cmd_search_again);
182 Set(L"N", cmd_search_again_rev);
183 Set(L"G", cmd_oldest_history_bol);
184 Set(L"g", cmd_return_history_bol);
185 Set(L"j", cmd_next_history_bol);
186 Set(L"+", cmd_next_history_bol);
187 Set(Key_down, cmd_next_history_bol);
188 Set(Key_c_n, cmd_next_history_bol);
189 Set(L"k", cmd_prev_history_bol);
190 Set(L"-", cmd_prev_history_bol);
191 Set(Key_up, cmd_prev_history_bol);
192 Set(Key_c_p, cmd_prev_history_bol);
193 le_modes[LE_MODE_VI_COMMAND].keymap = t;
194
195 le_modes[LE_MODE_VI_SEARCH].default_command = cmd_srch_self_insert;
196 t = trie_create();
197 Set(Key_c_v, cmd_expect_verbatim);
198 Set(Key_interrupt, cmd_abort_line);
199 Set(Key_c_c, cmd_abort_line);
200 Set(Key_c_l, cmd_redraw_all);
201 Set(Key_backslash, cmd_srch_self_insert);
202 Set(Key_backspace, cmd_srch_backward_delete_char);
203 Set(Key_erase, cmd_srch_backward_delete_char);
204 Set(Key_c_h, cmd_srch_backward_delete_char);
205 Set(Key_kill, cmd_srch_backward_delete_line);
206 Set(Key_c_u, cmd_srch_backward_delete_line);
207 Set(Key_c_j, cmd_srch_accept_search);
208 Set(Key_c_m, cmd_srch_accept_search);
209 Set(Key_escape, cmd_srch_abort_search);
210 le_modes[LE_MODE_VI_SEARCH].keymap = t;
211
212 le_modes[LE_MODE_EMACS].default_command = cmd_self_insert;
213 t = trie_create();
214 Set(Key_backslash, cmd_self_insert);
215 Set(Key_escape Key_c_i, cmd_insert_tab);
216 Set(Key_c_q, cmd_expect_verbatim);
217 Set(Key_c_v, cmd_expect_verbatim);
218 Set(Key_escape "0", cmd_digit_argument);
219 Set(Key_escape "1", cmd_digit_argument);
220 Set(Key_escape "2", cmd_digit_argument);
221 Set(Key_escape "3", cmd_digit_argument);
222 Set(Key_escape "4", cmd_digit_argument);
223 Set(Key_escape "5", cmd_digit_argument);
224 Set(Key_escape "6", cmd_digit_argument);
225 Set(Key_escape "7", cmd_digit_argument);
226 Set(Key_escape "8", cmd_digit_argument);
227 Set(Key_escape "9", cmd_digit_argument);
228 Set(Key_escape "-", cmd_digit_argument);
229 Set(Key_c_j, cmd_accept_line);
230 Set(Key_c_m, cmd_accept_line);
231 Set(Key_interrupt, cmd_abort_line);
232 Set(Key_c_c, cmd_abort_line);
233 Set(Key_eof, cmd_eof_or_delete);
234 Set(Key_c_d, cmd_eof_or_delete);
235 Set(Key_escape "#", cmd_accept_with_hash);
236 Set(Key_c_l, cmd_redraw_all);
237 Set(Key_right, cmd_forward_char);
238 Set(Key_c_f, cmd_forward_char);
239 Set(Key_left, cmd_backward_char);
240 Set(Key_c_b, cmd_backward_char);
241 Set(Key_escape "f", cmd_forward_emacsword);
242 Set(Key_escape "F", cmd_forward_emacsword);
243 Set(Key_escape "b", cmd_backward_emacsword);
244 Set(Key_escape "B", cmd_backward_emacsword);
245 Set(Key_home, cmd_beginning_of_line);
246 Set(Key_c_a, cmd_beginning_of_line);
247 Set(Key_end, cmd_end_of_line);
248 Set(Key_c_e, cmd_end_of_line);
249 Set(Key_c_rb, cmd_find_char);
250 Set(Key_escape Key_c_rb, cmd_find_char_rev);
251 Set(Key_delete, cmd_delete_char);
252 Set(Key_backspace, cmd_backward_delete_char);
253 Set(Key_erase, cmd_backward_delete_char);
254 Set(Key_c_h, cmd_backward_delete_char);
255 Set(Key_escape "d", cmd_kill_emacsword);
256 Set(Key_escape "D", cmd_kill_emacsword);
257 Set(Key_escape Key_backspace, cmd_backward_kill_emacsword);
258 Set(Key_escape Key_erase, cmd_backward_kill_emacsword);
259 Set(Key_escape Key_c_h, cmd_backward_kill_emacsword);
260 Set(Key_c_k, cmd_forward_kill_line);
261 Set(Key_kill, cmd_backward_kill_line);
262 Set(Key_c_u, cmd_backward_kill_line);
263 Set(Key_c_x Key_backspace, cmd_backward_kill_line);
264 Set(Key_c_x Key_erase, cmd_backward_kill_line);
265 Set(Key_c_y, cmd_put_left);
266 Set(Key_escape "y", cmd_put_pop);
267 Set(Key_escape "Y", cmd_put_pop);
268 Set(Key_c_w, cmd_backward_kill_bigword);
269 Set(Key_c_ul, cmd_undo);
270 Set(Key_c_x Key_c_u, cmd_undo);
271 Set(Key_c_x Key_kill, cmd_undo);
272 Set(Key_escape Key_c_r, cmd_undo_all);
273 Set(Key_escape "r", cmd_undo_all);
274 Set(Key_escape "R", cmd_undo_all);
275 Set(Key_tab, cmd_complete_next_candidate);
276 Set(Key_btab, cmd_complete_prev_candidate);
277 Set(Key_escape "=", cmd_complete_list);
278 Set(Key_escape "?", cmd_complete_list);
279 Set(Key_escape "*", cmd_complete_all);
280 Set(Key_c_t, cmd_emacs_transpose_chars);
281 Set(Key_escape "t", cmd_emacs_transpose_words);
282 Set(Key_escape "T", cmd_emacs_transpose_words);
283 Set(Key_escape "l", cmd_emacs_downcase_word);
284 Set(Key_escape "L", cmd_emacs_downcase_word);
285 Set(Key_escape "u", cmd_emacs_upcase_word);
286 Set(Key_escape "U", cmd_emacs_upcase_word);
287 Set(Key_escape "c", cmd_emacs_capitalize_word);
288 Set(Key_escape "C", cmd_emacs_capitalize_word);
289 Set(Key_escape Key_backslash, cmd_emacs_delete_horizontal_space);
290 Set(Key_escape " ", cmd_emacs_just_one_space);
291 Set(Key_c_s, cmd_emacs_search_forward);
292 Set(Key_c_r, cmd_emacs_search_backward);
293 Set(Key_escape "<", cmd_oldest_history_eol);
294 Set(Key_escape ">", cmd_return_history_eol);
295 Set(Key_down, cmd_next_history_eol);
296 Set(Key_c_n, cmd_next_history_eol);
297 Set(Key_up, cmd_prev_history_eol);
298 Set(Key_c_p, cmd_prev_history_eol);
299 le_modes[LE_MODE_EMACS].keymap = t;
300
301 le_modes[LE_MODE_EMACS_SEARCH].default_command = cmd_srch_self_insert;
302 t = trie_create();
303 Set(Key_c_v, cmd_expect_verbatim);
304 Set(Key_c_m, cmd_accept_line);
305 Set(Key_interrupt, cmd_abort_line);
306 Set(Key_c_c, cmd_abort_line);
307 Set(Key_c_l, cmd_redraw_all);
308 Set(Key_backslash, cmd_srch_self_insert);
309 Set(Key_backspace, cmd_srch_backward_delete_char);
310 Set(Key_erase, cmd_srch_backward_delete_char);
311 Set(Key_c_h, cmd_srch_backward_delete_char);
312 Set(Key_kill, cmd_srch_backward_delete_line);
313 Set(Key_c_u, cmd_srch_backward_delete_line);
314 Set(Key_c_s, cmd_srch_continue_forward);
315 Set(Key_c_r, cmd_srch_continue_backward);
316 Set(Key_c_j, cmd_srch_accept_search);
317 Set(Key_escape, cmd_srch_accept_search);
318 Set(Key_c_g, cmd_srch_abort_search);
319 le_modes[LE_MODE_EMACS_SEARCH].keymap = t;
320
321 le_modes[LE_MODE_CHAR_EXPECT].default_command = cmd_expect_char;
322 t = trie_create();
323 Set(Key_c_v, cmd_expect_verbatim);
324 Set(Key_interrupt, cmd_abort_line);
325 Set(Key_c_c, cmd_abort_line);
326 Set(Key_backslash, cmd_expect_char);
327 Set(Key_escape, cmd_abort_expect_char);
328 le_modes[LE_MODE_CHAR_EXPECT].keymap = t;
329
330 #undef Set
331 }
332
333 /* Sets the editing mode to the one specified by `id'. */
le_set_mode(le_mode_id_T id)334 void le_set_mode(le_mode_id_T id)
335 {
336 assert(id < LE_MODE_N);
337 le_current_mode = le_id_to_mode(id);
338 }
339
340 /* Generates completion candidates for editing command names matching the
341 * pattern. */
342 /* The prototype of this function is declared in "complete.h". */
generate_bindkey_candidates(const le_compopt_T * compopt)343 void generate_bindkey_candidates(const le_compopt_T *compopt)
344 {
345 if (!(compopt->type & CGT_BINDKEY))
346 return;
347
348 le_compdebug("adding lineedit command name candidates");
349 if (!le_compile_cpatterns(compopt))
350 return;
351
352 for (size_t i = 0; i < sizeof commands / sizeof *commands; i++)
353 if (le_match_comppatterns(compopt, commands[i].name))
354 le_new_candidate(CT_BINDKEY,
355 malloc_mbstowcs(commands[i].name), NULL, compopt);
356 }
357
358
359 /********** Built-in **********/
360
361 static int print_all_commands(void);
362 static int set_key_binding(
363 le_mode_id_T mode, const wchar_t *keyseq, const wchar_t *commandname)
364 __attribute__((nonnull));
365 static le_command_func_T *get_command_from_name(const char *name)
366 __attribute__((nonnull,pure));
367 static int command_name_compare(const void *p1, const void *p2)
368 __attribute__((nonnull,pure));
369 static int print_binding(le_mode_id_T mode, const wchar_t *keyseq)
370 __attribute__((nonnull));
371 static int print_binding_main(
372 void *mode, const wchar_t *keyseq, le_command_func_T *cmd)
373 __attribute__((nonnull));
374 static const char *get_command_name(le_command_func_T *command)
375 __attribute__((nonnull,const));
376
377 /* Options for the "bindkey" built-in. */
378 const struct xgetopt_T bindkey_options[] = {
379 { L'v', L"vi-insert", OPTARG_NONE, false, NULL, },
380 { L'a', L"vi-command", OPTARG_NONE, false, NULL, },
381 { L'e', L"emacs", OPTARG_NONE, false, NULL, },
382 { L'l', L"list", OPTARG_NONE, false, NULL, },
383 #if YASH_ENABLE_HELP
384 { L'-', L"help", OPTARG_NONE, false, NULL, },
385 #endif
386 { L'\0', NULL, 0, false, NULL, },
387 };
388
389 /* The "bindkey" built-in, which accepts the following options:
390 * -v: select the "vi-insert" mode
391 * -a: select the "vi-command" mode
392 * -e: select the "emacs" mode
393 * -l: list names of available commands */
bindkey_builtin(int argc,void ** argv)394 int bindkey_builtin(int argc, void **argv)
395 {
396 bool list = false;
397 le_mode_id_T mode = LE_MODE_N;
398
399 const struct xgetopt_T *opt;
400 xoptind = 0;
401 while ((opt = xgetopt(argv, bindkey_options, 0)) != NULL) {
402 switch (opt->shortopt) {
403 case L'a': mode = LE_MODE_VI_COMMAND; break;
404 case L'e': mode = LE_MODE_EMACS; break;
405 case L'v': mode = LE_MODE_VI_INSERT; break;
406 case L'l': list = true; break;
407 #if YASH_ENABLE_HELP
408 case L'-':
409 return print_builtin_help(ARGV(0));
410 #endif
411 default:
412 return Exit_ERROR;
413 }
414 }
415
416 le_keymap_init();
417
418 if (list) {
419 if (!validate_operand_count(argc - xoptind, 0, 0))
420 return Exit_ERROR;
421 if (mode != LE_MODE_N) {
422 xerror(0, Ngt("option combination is invalid"));
423 return Exit_ERROR;
424 }
425 return print_all_commands();
426 }
427
428 if (mode == LE_MODE_N) {
429 xerror(0, Ngt("no option is specified"));
430 return Exit_ERROR;
431 }
432
433 switch (argc - xoptind) {
434 case 0:;
435 /* print all key bindings */
436 le_mode_T *m = le_id_to_mode(mode);
437 return trie_foreachw(m->keymap, print_binding_main, m);
438 case 1:
439 return print_binding(mode, ARGV(xoptind));
440 case 2:
441 return set_key_binding(mode, ARGV(xoptind), ARGV(xoptind + 1));
442 default:
443 return too_many_operands_error(2);
444 }
445 }
446
447 /* Prints all available commands to the standard output. */
print_all_commands(void)448 int print_all_commands(void)
449 {
450 for (size_t i = 0; i < sizeof commands / sizeof *commands; i++)
451 if (!xprintf("%s\n", commands[i].name))
452 return Exit_FAILURE;
453 return Exit_SUCCESS;
454 }
455
456 /* Binds the specified key sequence to the specified command.
457 * If `commandname' is L"-", the binding is removed.
458 * If the specified command is not found, it is an error. */
set_key_binding(le_mode_id_T mode,const wchar_t * keyseq,const wchar_t * commandname)459 int set_key_binding(
460 le_mode_id_T mode, const wchar_t *keyseq, const wchar_t *commandname)
461 {
462 if (keyseq[0] == L'\0') {
463 xerror(0, Ngt("cannot bind an empty key sequence"));
464 return Exit_FAILURE;
465 }
466
467 if (wcscmp(commandname, L"-") == 0) {
468 /* delete key binding */
469 register trie_T *t = le_modes[mode].keymap;
470 t = trie_removew(t, keyseq);
471 le_modes[mode].keymap = t;
472 } else {
473 /* set key binding */
474 char *mbsname = malloc_wcstombs(commandname);
475 if (mbsname == NULL) {
476 xerror(EILSEQ, Ngt("unexpected error"));
477 return Exit_FAILURE;
478 }
479
480 le_command_func_T *cmd = get_command_from_name(mbsname);
481 free(mbsname);
482 if (cmd) {
483 register trie_T *t = le_modes[mode].keymap;
484 t = trie_setw(t, keyseq, (trievalue_T) { .cmdfunc = cmd });
485 le_modes[mode].keymap = t;
486 } else {
487 xerror(0, Ngt("no such editing command `%ls'"), commandname);
488 return Exit_FAILURE;
489 }
490 }
491 return Exit_SUCCESS;
492 }
493
494 /* Returns the command function of the specified name.
495 * Returns a null pointer if no such command is found. */
get_command_from_name(const char * name)496 le_command_func_T *get_command_from_name(const char *name)
497 {
498 struct command_name_pair *cnp = bsearch(name, commands,
499 sizeof commands / sizeof *commands,
500 sizeof *commands,
501 command_name_compare);
502
503 return (cnp != NULL) ? cnp->command : 0;
504 }
505
command_name_compare(const void * p1,const void * p2)506 int command_name_compare(const void *p1, const void *p2)
507 {
508 return strcmp((const char *) p1,
509 ((const struct command_name_pair *) p2)->name);
510 }
511
512 /* Prints the binding for the given key sequence. */
print_binding(le_mode_id_T mode,const wchar_t * keyseq)513 int print_binding(le_mode_id_T mode, const wchar_t *keyseq)
514 {
515 trieget_T tg = trie_getw(le_modes[mode].keymap, keyseq);
516
517 if ((tg.type & TG_EXACTMATCH) && tg.matchlength == wcslen(keyseq)) {
518 return print_binding_main(
519 le_id_to_mode(mode), keyseq, tg.value.cmdfunc);
520 } else {
521 xerror(0, Ngt("key sequence `%ls' is not bound"), keyseq);
522 return Exit_FAILURE;
523 }
524 }
525
526 /* Prints a command to restore the specified key binding.
527 * `mode' must be a pointer to one of the modes in `le_modes'. */
print_binding_main(void * mode,const wchar_t * keyseq,le_command_func_T * cmd)528 int print_binding_main(
529 void *mode, const wchar_t *keyseq, le_command_func_T *cmd)
530 {
531 const char *format;
532 char modechar;
533 wchar_t *keyseqquote;
534 const char *commandname;
535
536 switch (le_mode_to_id(mode)) {
537 case LE_MODE_VI_INSERT: modechar = 'v'; break;
538 case LE_MODE_VI_COMMAND: modechar = 'a'; break;
539 case LE_MODE_VI_SEARCH: modechar = 'V'; break;
540 case LE_MODE_EMACS: modechar = 'e'; break;
541 case LE_MODE_EMACS_SEARCH: modechar = 'E'; break;
542 case LE_MODE_CHAR_EXPECT: modechar = 'c'; break;
543 default: assert(false);
544 }
545 if (keyseq[0] == L'-')
546 format = "bindkey -%c -- %ls %s\n";
547 else
548 format = "bindkey -%c %ls %s\n";
549 keyseqquote = quote_as_word(keyseq);
550 commandname = get_command_name(cmd);
551 xprintf(format, modechar, keyseqquote, commandname);
552 free(keyseqquote);
553 return (yash_error_message_count == 0) ? Exit_SUCCESS : Exit_FAILURE;
554 }
555
556 /* Returns the name of the specified command. */
get_command_name(le_command_func_T * command)557 const char *get_command_name(le_command_func_T *command)
558 {
559 for (size_t i = 0; i < sizeof commands / sizeof *commands; i++)
560 if (commands[i].command == command)
561 return commands[i].name;
562 return NULL;
563 }
564
565 #if YASH_ENABLE_HELP
566 const char bindkey_help[] = Ngt(
567 "set or print key bindings for line-editing"
568 );
569 const char bindkey_syntax[] = Ngt(
570 "\tbindkey -aev [key_sequence [command]]\n"
571 "\tbindkey -l\n"
572 );
573 #endif
574
575
576 /* vim: set ts=8 sts=4 sw=4 noet tw=80: */
577