1 /* keybinds.c -- functions keybindings configuration */
2
3 /*
4 * This file is part of CliFM
5 *
6 * Copyright (C) 2016-2021, L. Abramovich <johndoe.arch@outlook.com>
7 * All rights reserved.
8
9 * CliFM is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * CliFM is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
22 * MA 02110-1301, USA.
23 */
24
25 #include "helpers.h"
26
27 #include <stdio.h>
28 #include <sys/stat.h>
29 #ifdef __OpenBSD__
30 typedef char *rl_cpvfunc_t;
31 #include <ereadline/readline/readline.h>
32 #else
33 #include <readline/readline.h>
34 #endif
35 #ifdef __TINYC__
36 /* Silence a tcc warning. We don't use CTRL anyway */
37 #undef CTRL
38 #endif
39 #include <termios.h>
40 #include <unistd.h>
41 #ifdef __NetBSD__
42 #include <string.h>
43 #endif
44 #include <dirent.h>
45
46 #include "aux.h"
47 #include "config.h"
48 #include "exec.h"
49 #include "keybinds.h"
50 #include "listing.h"
51 #include "mime.h"
52 #include "misc.h"
53 #include "profiles.h"
54 #include "prompt.h"
55 #include "messages.h"
56 #include "strings.h"
57 #include "readline.h"
58 #include "file_operations.h"
59
60 #ifndef _NO_SUGGESTIONS
61 #include "suggestions.h"
62 #endif
63
64 #ifndef _NO_HIGHLIGHT
65 #include "highlight.h"
66 #endif
67
68 int accept_first_word = 0;
69
70 int
kbinds_reset(void)71 kbinds_reset(void)
72 {
73 int exit_status = EXIT_SUCCESS;
74 struct stat file_attrib;
75
76 if (stat(kbinds_file, &file_attrib) == -1) {
77 exit_status = create_kbinds_file();
78 } else {
79 char *cmd[] = {"rm", kbinds_file, NULL};
80 if (launch_execve(cmd, FOREGROUND, E_NOFLAG) == EXIT_SUCCESS)
81 exit_status = create_kbinds_file();
82 else
83 exit_status = EXIT_FAILURE;
84 }
85
86 if (exit_status == EXIT_SUCCESS)
87 _err('n', PRINT_PROMPT, _("%s: Restart the program for changes "
88 "to take effect\n"), PROGRAM_NAME);
89
90 return exit_status;
91 }
92
93 static int
kbinds_edit(void)94 kbinds_edit(void)
95 {
96 if (xargs.stealth_mode == 1) {
97 printf("%s: Access to configuration files is not allowed in "
98 "stealth mode\n", PROGRAM_NAME);
99 return EXIT_SUCCESS;
100 }
101
102 if (!kbinds_file)
103 return EXIT_FAILURE;
104
105 struct stat file_attrib;
106 if (stat(kbinds_file, &file_attrib) == -1) {
107 create_kbinds_file();
108 stat(kbinds_file, &file_attrib);
109 }
110
111 time_t mtime_bfr = (time_t)file_attrib.st_mtime;
112
113 open_in_foreground = 1;
114 int ret = open_file(kbinds_file);
115 open_in_foreground = 0;
116 if (ret != EXIT_SUCCESS)
117 return EXIT_FAILURE;
118
119 stat(kbinds_file, &file_attrib);
120 if (mtime_bfr == (time_t)file_attrib.st_mtime)
121 return EXIT_SUCCESS;
122
123 _err('n', PRINT_PROMPT, _("%s: Restart the program for changes to "
124 "take effect\n"), PROGRAM_NAME);
125 return EXIT_SUCCESS;
126 }
127
128 int
kbinds_function(char ** args)129 kbinds_function(char **args)
130 {
131 if (!args)
132 return EXIT_FAILURE;
133
134 if (!args[1]) {
135 size_t i;
136 for (i = 0; i < kbinds_n; i++) {
137 printf("%s: %s\n", kbinds[i].key, kbinds[i].function);
138 }
139
140 return EXIT_SUCCESS;
141 }
142
143 if (*args[1] == '-' && strcmp(args[1], "--help") == 0) {
144 puts(_(KB_USAGE));
145 return EXIT_SUCCESS;
146 }
147
148 if (*args[1] == 'e' && strcmp(args[1], "edit") == 0)
149 return kbinds_edit();
150
151 if (*args[1] == 'r' && strcmp(args[1], "reset") == 0)
152 return kbinds_reset();
153
154 if (*args[1] == 'r' && strcmp(args[1], "readline") == 0) {
155 rl_function_dumper(1);
156 return EXIT_SUCCESS;
157 }
158
159 fprintf(stderr, "%s\n", _(KB_USAGE));
160 return EXIT_FAILURE;
161 }
162
163 /* Store keybinds from the keybinds file into a struct */
164 int
load_keybinds(void)165 load_keybinds(void)
166 {
167 if (!config_ok)
168 return EXIT_FAILURE;
169
170 /* Free the keybinds struct array */
171 if (kbinds_n) {
172 int i = (int)kbinds_n;
173
174 while (--i >= 0) {
175 free(kbinds[i].function);
176 free(kbinds[i].key);
177 }
178
179 free(kbinds);
180 kbinds = (struct kbinds_t *)xnmalloc(1, sizeof(struct kbinds_t));
181 kbinds_n = 0;
182 }
183
184 /* Open the keybinds file */
185 FILE *fp = fopen(kbinds_file, "r");
186 if (!fp)
187 return EXIT_FAILURE;
188
189 size_t line_size = 0;
190 char *line = (char *)NULL;
191 ssize_t line_len = 0;
192
193 while ((line_len = getline(&line, &line_size, fp)) > 0) {
194 if (!line || !*line || *line == '#' || *line == '\n')
195 continue;
196
197 if (line[line_len - 1] == '\n')
198 line[line_len - 1] = '\0';
199
200 char *tmp = (char *)NULL;
201 tmp = strchr(line, ':');
202 if (!tmp || !*(tmp + 1))
203 continue;
204
205 /* Now copy left and right value of each keybind into the
206 * keybinds struct */
207 kbinds = xrealloc(kbinds, (kbinds_n + 1) * sizeof(struct kbinds_t));
208 kbinds[kbinds_n].key = savestring(tmp + 1, strlen(tmp + 1));
209
210 *tmp = '\0';
211
212 kbinds[kbinds_n++].function = savestring(line, strlen(line));
213 }
214
215 free(line);
216 return EXIT_SUCCESS;
217 }
218
219 /* Runs any command recognized by CliFM via a keybind. Example:
220 * keybind_exec_cmd("sel *") */
221 int
keybind_exec_cmd(char * str)222 keybind_exec_cmd(char *str)
223 {
224 size_t old_args = args_n;
225 args_n = 0;
226
227 #ifndef _NO_SUGGESTIONS
228 if (suggestion.printed && suggestion_buf)
229 free_suggestion();
230 #endif
231
232 int exit_status = EXIT_FAILURE;
233 char **cmd = parse_input_str(str);
234 putchar('\n');
235
236 if (cmd) {
237 exit_status = exec_cmd(cmd);
238
239 /* While in the bookmarks or mountpoints screen, the kbind_busy
240 * flag will be set to 1 and no keybinding will work. Once the
241 * corresponding function exited, set the kbind_busy flag to zero,
242 * so that keybindings work again */
243 if (kbind_busy)
244 kbind_busy = 0;
245
246 int i = (int)args_n + 1;
247 while (--i >= 0)
248 free(cmd[i]);
249 free(cmd);
250
251 /* This call to prompt() just updates the prompt in case it was
252 * modified, for example, in case of chdir, files selection, and
253 * so on */
254 char *buf = prompt();
255 free(buf);
256 }
257
258 args_n = old_args;
259 return exit_status;
260 }
261
262 static int
run_kb_cmd(char * cmd)263 run_kb_cmd(char *cmd)
264 {
265 if (!cmd || !*cmd)
266 return EXIT_FAILURE;
267
268 if (kbind_busy)
269 return EXIT_SUCCESS;
270
271 keybind_exec_cmd(cmd);
272 rl_reset_line_state();
273 return EXIT_SUCCESS;
274 }
275
276 /* Retrieve the key sequence associated to FUNCTION */
277 static char *
find_key(char * function)278 find_key(char *function)
279 {
280 if (!kbinds_n)
281 return (char *)NULL;
282
283 int n = (int)kbinds_n;
284 while (--n >= 0) {
285 if (*function != *kbinds[n].function)
286 continue;
287 if (strcmp(function, kbinds[n].function) == 0)
288 return kbinds[n].key;
289 }
290
291 return (char *)NULL;
292 }
293
294 /* Prepend sudo/doas to the current input string */
295 static int
rl_prepend_sudo(int count,int key)296 rl_prepend_sudo(int count, int key)
297 {
298 UNUSED(count); UNUSED(key);
299 int free_s = 1;
300 size_t len = 0;
301 char *t = getenv("CLIFM_SUDO_CMD"),
302 *s = (char *)NULL;
303
304 if (t) {
305 len = strlen(t);
306 if (t[len - 1] != ' ') {
307 s = (char *)xnmalloc(len + 2, sizeof(char));
308 sprintf(s, "%s ", t);
309 len++;
310 } else {
311 s = t;
312 free_s = 0;
313 }
314 } else {
315 len = strlen(DEF_SUDO_CMD) + 1;
316 s = (char *)xnmalloc(len + 1, sizeof(char));
317 sprintf(s, "%s ", DEF_SUDO_CMD);
318 }
319
320 int p = rl_point;
321 if (*rl_line_buffer == *s
322 && strncmp(rl_line_buffer, s, len) == 0) {
323 rl_delete_text(0, (int)len);
324 rl_point = p - (int)len;
325 } else {
326 rl_point = 0;
327 rl_insert_text(s);
328 rl_point = p + (int)len;
329 }
330
331 if (free_s)
332 free(s);
333
334 #ifndef _NO_SUGGESTIONS
335 if (suggestion.offset == 0 && suggestion_buf) {
336 int r = rl_point;
337 rl_point = rl_end;
338 clear_suggestion(CS_FREEBUF);
339 rl_point = r;
340 }
341 #endif
342 return EXIT_SUCCESS;
343 }
344
345 static int
rl_create_file(int count,int key)346 rl_create_file(int count, int key)
347 {
348 UNUSED(count); UNUSED(key);
349 return run_kb_cmd("n");
350 }
351
352 #ifndef _NO_SUGGESTIONS
353 /* Insert the accepted suggestion into the current input line
354 * (highlighting words and special chars if syntax highlighting is enabled) */
355 static void
my_insert_text(char * text,char * s,const char _s)356 my_insert_text(char *text, char *s, const char _s)
357 {
358 #ifdef _NO_HIGHLIGHT
359 UNUSED(s); UNUSED(_s);
360 #endif
361
362 if (!text || !*text)
363 return;
364
365 if (wrong_cmd || cur_color == hq_c)
366 goto INSERT_TEXT;
367
368 #ifndef _NO_HIGHLIGHT
369 if (highlight) {
370 /* Hide the cursor to minimize flickering */
371 fputs("\x1b[?25l", stdout);
372 /* Set text color to default */
373 fputs(tx_c, stdout);
374 cur_color = tx_c;
375 char *t = text;
376 size_t i;
377
378 /* We only need to redisplay first suggested word if it contains
379 * a highlighting char and it is not preceded by a space */
380 int redisplay = 0;
381 if (accept_first_word) {
382 for (i = 0; t[i]; i++) {
383 if (t[i] >= '0' && t[i] <= '9') {
384 if (!i || t[i - 1] != ' ') {
385 redisplay = 1;
386 break;
387 }
388 }
389 switch(t[i]) {
390 case '/': /* fallthrough */
391 case '"': /* fallthrough */
392 case '\'': /* fallthrough */
393 case '&': /* fallthrough */
394 case '|': /* fallthrough */
395 case ';': /* fallthrough */
396 case '>': /* fallthrough */
397 case '(': /* fallthrough */
398 case '[': /* fallthrough */
399 case '{': /* fallthrough */
400 case ')': /* fallthrough */
401 case ']': /* fallthrough */
402 case '}': /* fallthrough */
403 case '$': /* fallthrough */
404 case '-': /* fallthrough */
405 case '~': /* fallthrough */
406 case '*': /* fallthrough */
407 case '#':
408 if (!i || t[i - 1] != ' ')
409 redisplay = 1;
410 break;
411 default: break;
412 }
413 if (redisplay)
414 break;
415 }
416 }
417
418 char q[PATH_MAX];
419 int l = 0;
420 for (i = 0; t[i]; i++) {
421 rl_highlight(t, i, SET_COLOR);
422 if (t[i] < 0) {
423 q[l++] = t[i];
424 if (t[i + 1] >= 0) {
425 q[l] = '\0';
426 l = 0;
427 rl_insert_text(q);
428 rl_redisplay();
429 }
430 continue;
431 }
432 q[0] = t[i];
433 q[1] = '\0';
434 rl_insert_text(q);
435 if (!accept_first_word || redisplay)
436 rl_redisplay();
437 }
438
439 if (s && redisplay) {
440 /* 1) rl_redisplay removes the suggestion from the current line
441 * 2) We need rl_redisplay to correctly print highlighting colors
442 * 3) We need to keep the suggestion when accepting only
443 * the first suggested word.
444 * In other words, if we correctly print colors, we lose the
445 * suggestion.
446 * As a workaround, let's reprint the suggestion */
447 size_t slen = strlen(suggestion_buf);
448 *s = _s ? _s : ' ';
449 print_suggestion(suggestion_buf, slen + 1, suggestion.color);
450 *s = '\0';
451 }
452
453 fputs("\x1b[?25h", stdout);
454 } else
455 #endif
456 INSERT_TEXT:
457 {
458 rl_insert_text(text);
459 }
460 }
461
462 static int
rl_accept_suggestion(int count,int key)463 rl_accept_suggestion(int count, int key)
464 {
465 UNUSED(count); UNUSED(key);
466 if (kbind_busy) {
467 /* If not at the end of the typed string, just move the cursor
468 * forward one column */
469 if (rl_point < rl_end)
470 rl_point++;
471 return EXIT_SUCCESS;
472 }
473
474 if (!wrong_cmd && cur_color != hq_c) {
475 cur_color = tx_c;
476 fputs(tx_c, stdout);
477 }
478
479 /* Only accept the current suggestion if the cursor is at the end
480 * of the line typed so far */
481 if (!suggestions || rl_point != rl_end || !suggestion_buf) {
482 if (rl_point < rl_end) {
483 /* Just move the cursor forward one column */
484 int mlen = mblen(rl_line_buffer + rl_point, MB_LEN_MAX);
485 rl_point += mlen;
486 }
487 return EXIT_SUCCESS;
488 }
489
490 /* If accepting the first suggested word, accept only up to next
491 * slash or space */
492 char *s = (char *)NULL, _s = 0;
493 int slash = 0;
494 if (accept_first_word) {
495 size_t i = 0;
496 char *p = suggestion_buf + (rl_point - suggestion.offset);
497 /* Skip leading spaces */
498 while (*(p + i) == ' ')
499 i++;
500
501 /* Trim the suggestion up to first slash or space */
502 s = strchr(p + i, '/');
503 /* If the slash is immediately preceded by a space, move to
504 * the next slash */
505 if (s && s != p && *(s - 1) == ' ') {
506 char *ss = strchr(s + 1, '/');
507 if (ss)
508 s = ss;
509 }
510 char *sp = strchr(p + i, ' ');
511 if (s) {
512 /* If there is a space somewhere before the slash */
513 if (sp && sp < s) {
514 s = sp;
515 } else {
516 /* In case of slash, keep a copy of the next char, if any:
517 * we cannot know in advance what comes after the slash */
518 if (*(++s))
519 _s = *s;
520 slash = 1;
521 }
522 } else if (sp) {
523 s = sp;
524 }
525
526 if (s && (slash ? *s : *(s + 1)) && s != p) {
527 *s = '\0';
528 } else {
529 /* Last word: neither space nor slash */
530 size_t len = strlen(suggestion_buf);
531 if (suggestion_buf[len - 1] != '/'
532 && suggestion_buf[len - 1] != ' ')
533 suggestion.type = NO_SUG;
534 accept_first_word = 0;
535 s = (char *)NULL;
536 }
537 }
538
539 rl_delete_text(suggestion.offset, rl_end);
540 rl_point = suggestion.offset;
541
542 if (!accept_first_word && (suggestion.type == BOOKMARK_SUG
543 || suggestion.type == ALIAS_SUG || suggestion.type == ELN_SUG
544 || suggestion.type == JCMD_SUG || suggestion.type == JCMD_SUG_NOACD))
545 clear_suggestion(CS_KEEPBUF);
546
547 /* Complete according to the suggestion type */
548 switch(suggestion.type) {
549
550 case JCMD_SUG: /* fallthrough */
551 case BOOKMARK_SUG: /* fallthrough */
552 case COMP_SUG: /* fallthrough */
553 case ELN_SUG: /* fallthrough */
554 case FILE_SUG: {
555 char *tmp = (char *)NULL;
556 size_t i, isquote = 0, backslash = 0;
557 for (i = 0; suggestion_buf[i]; i++) {
558 if (is_quote_char(suggestion_buf[i]))
559 isquote = 1;
560 if (suggestion_buf[i] == '\\') {
561 backslash = 1;
562 break;
563 }
564 }
565 if (isquote && !backslash)
566 tmp = escape_str(suggestion_buf);
567
568 if (tmp) {
569 rl_insert_text(tmp);
570 free(tmp);
571 } else {
572 my_insert_text(suggestion_buf, NULL, 0);
573 }
574 if (suggestion.filetype != DT_DIR)
575 rl_stuff_char(' ');
576 suggestion.type = NO_SUG;
577 }
578 break;
579
580 case FIRST_WORD:
581 my_insert_text(suggestion_buf, s, _s); break;
582
583 case JCMD_SUG_NOACD: /* fallthrough */
584 case SEL_SUG: /* fallthrough */
585 case HIST_SUG:
586 my_insert_text(suggestion_buf, NULL, 0); break;
587
588 case VAR_SUG:
589 my_insert_text(suggestion_buf, NULL, 0);
590 rl_stuff_char(' ');
591 break;
592
593 default:
594 my_insert_text(suggestion_buf, NULL, 0);
595 rl_stuff_char(' ');
596 break;
597 }
598
599 /* Move the cursor to the end of the line */
600 rl_point = rl_end;
601 if (!accept_first_word) {
602 suggestion.printed = 0;
603 free(suggestion_buf);
604 suggestion_buf = (char *)NULL;
605 } else {
606 if (s) {
607 /* Reinsert the char we removed to print only the first word */
608 if (slash)
609 *s = _s;
610 else
611 *s = ' ';
612 }
613 accept_first_word = 0;
614 }
615
616 return EXIT_SUCCESS;
617 }
618
619 static int
rl_accept_first_word(int count,int key)620 rl_accept_first_word(int count, int key)
621 {
622 /* Accepting the first suggested word is not supported for ELN's,
623 * bookmarks and aliases names */
624 if (suggestion.type != ELN_SUG && suggestion.type != BOOKMARK_SUG
625 && suggestion.type != ALIAS_SUG && suggestion.type != JCMD_SUG
626 && suggestion.type != JCMD_SUG_NOACD) {
627 accept_first_word = 1;
628 suggestion.type = FIRST_WORD;
629 }
630 return rl_accept_suggestion(count, key);
631 }
632 #endif /* !_NO_SUGGESTIONS */
633
634 static int
rl_refresh(int count,int key)635 rl_refresh(int count, int key)
636 {
637 UNUSED(count); UNUSED(key);
638 if (kbind_busy)
639 return EXIT_SUCCESS;
640
641 if (clear_screen)
642 CLEAR;
643 keybind_exec_cmd("rf");
644 rl_reset_line_state();
645 return EXIT_SUCCESS;
646 }
647
648 static int
rl_parent_dir(int count,int key)649 rl_parent_dir(int count, int key)
650 {
651 UNUSED(count); UNUSED(key);
652 /* If already root dir, do nothing */
653 if (*ws[cur_ws].path == '/' && !ws[cur_ws].path[1])
654 return EXIT_SUCCESS;
655 return run_kb_cmd("cd ..");
656 }
657
658 static int
rl_root_dir(int count,int key)659 rl_root_dir(int count, int key)
660 {
661 UNUSED(count); UNUSED(key);
662 /* If already root dir, do nothing */
663 if (*ws[cur_ws].path == '/' && !ws[cur_ws].path[1])
664 return EXIT_SUCCESS;
665 return run_kb_cmd("cd /");
666 }
667
668 static int
rl_home_dir(int count,int key)669 rl_home_dir(int count, int key)
670 {
671 UNUSED(count); UNUSED(key);
672 /* If already in home, do nothing */
673 if (*ws[cur_ws].path == *user.home && strcmp(ws[cur_ws].path, user.home) == 0)
674 return EXIT_SUCCESS;
675 return run_kb_cmd("cd");
676 }
677
678 static int
rl_next_dir(int count,int key)679 rl_next_dir(int count, int key)
680 {
681 UNUSED(count); UNUSED(key);
682 /* If already at the end of dir hist, do nothing */
683 if (dirhist_cur_index + 1 == dirhist_total_index)
684 return EXIT_SUCCESS;
685 return run_kb_cmd("f");
686 }
687
688 static int
rl_first_dir(int count,int key)689 rl_first_dir(int count, int key)
690 {
691 UNUSED(count); UNUSED(key);
692 /* If already at the beginning of dir hist, do nothing */
693 if (dirhist_cur_index == 0)
694 return EXIT_SUCCESS;
695
696 return run_kb_cmd("b !1");
697 }
698
699 static int
rl_last_dir(int count,int key)700 rl_last_dir(int count, int key)
701 {
702 UNUSED(count); UNUSED(key);
703 if (kbind_busy)
704 return EXIT_SUCCESS;
705
706 /* If already at the end of dir hist, do nothing */
707 if (dirhist_cur_index + 1 == dirhist_total_index)
708 return EXIT_SUCCESS;
709
710 char cmd[PATH_MAX + 4];
711 sprintf(cmd, "b !%d", dirhist_total_index);
712 keybind_exec_cmd(cmd);
713 rl_reset_line_state();
714 return EXIT_SUCCESS;
715 }
716
717 static int
rl_previous_dir(int count,int key)718 rl_previous_dir(int count, int key)
719 {
720 UNUSED(count); UNUSED(key);
721 /* If already at the beginning of dir hist, do nothing */
722 if (dirhist_cur_index == 0)
723 return EXIT_SUCCESS;
724 return run_kb_cmd("b");
725 }
726
727 static int
rl_long(int count,int key)728 rl_long(int count, int key)
729 {
730 UNUSED(count); UNUSED(key);
731 if (kbind_busy)
732 return EXIT_SUCCESS;
733
734 long_view = long_view ? 0 : 1;
735
736 if (clear_screen)
737 CLEAR;
738 keybind_exec_cmd("rf");
739 rl_reset_line_state();
740 return EXIT_SUCCESS;
741 }
742
743 static int
rl_folders_first(int count,int key)744 rl_folders_first(int count, int key)
745 {
746 UNUSED(count); UNUSED(key);
747 if (kbind_busy)
748 return EXIT_SUCCESS;
749
750 #ifndef _NO_SUGGESTIONS
751 if (suggestion.printed && suggestion_buf)
752 free_suggestion();
753 #endif
754
755 list_folders_first = list_folders_first ? 0 : 1;
756
757 if (autols) {
758 if (clear_screen)
759 CLEAR;
760 free_dirlist();
761 /* Without this putchar(), the first entries of the directories
762 * list are printed in the prompt line */
763 putchar('\n');
764 list_dir();
765 }
766
767 rl_reset_line_state();
768 return EXIT_SUCCESS;
769 }
770
771 static int
rl_light(int count,int key)772 rl_light(int count, int key)
773 {
774 UNUSED(count); UNUSED(key);
775 if (kbind_busy)
776 return EXIT_SUCCESS;
777
778 light_mode = light_mode ? 0 : 1;
779
780 if (clear_screen)
781 CLEAR;
782 keybind_exec_cmd("rf");
783 rl_reset_line_state();
784 return EXIT_SUCCESS;
785 }
786
787 static int
rl_hidden(int count,int key)788 rl_hidden(int count, int key)
789 {
790 UNUSED(count); UNUSED(key);
791 if (kbind_busy)
792 return EXIT_SUCCESS;
793 #ifndef _NO_SUGGESTIONS
794 if (suggestion.printed && suggestion_buf)
795 free_suggestion();
796 #endif
797 show_hidden = show_hidden ? 0 : 1;
798
799 if (autols) {
800 if (clear_screen)
801 CLEAR;
802 free_dirlist();
803 putchar('\n');
804 list_dir();
805 }
806
807 rl_reset_line_state();
808 return EXIT_SUCCESS;
809 }
810
811 static int
rl_open_config(int count,int key)812 rl_open_config(int count, int key)
813 {
814 UNUSED(count); UNUSED(key);
815 return run_kb_cmd("edit");
816 }
817
818 static int
rl_open_keybinds(int count,int key)819 rl_open_keybinds(int count, int key)
820 {
821 UNUSED(count); UNUSED(key);
822 return run_kb_cmd("kb edit");
823 }
824
825 static int
rl_open_cscheme(int count,int key)826 rl_open_cscheme(int count, int key)
827 {
828 UNUSED(count); UNUSED(key);
829 return run_kb_cmd("cs e");
830 }
831
832 static int
rl_open_bm_file(int count,int key)833 rl_open_bm_file(int count, int key)
834 {
835 UNUSED(count); UNUSED(key);
836 return run_kb_cmd("bm edit");
837 }
838
839 static int
rl_open_jump_db(int count,int key)840 rl_open_jump_db(int count, int key)
841 {
842 UNUSED(count); UNUSED(key);
843 return run_kb_cmd("je");
844 }
845
846 static int
rl_open_mime(int count,int key)847 rl_open_mime(int count, int key)
848 {
849 UNUSED(count); UNUSED(key);
850 return run_kb_cmd("mm edit");
851 }
852
853 static int
rl_mountpoints(int count,int key)854 rl_mountpoints(int count, int key)
855 {
856 UNUSED(count); UNUSED(key);
857 /* Call the function only if it's not already running */
858 kbind_busy = 1;
859 keybind_exec_cmd("mp");
860 rl_reset_line_state();
861 return EXIT_SUCCESS;
862 }
863
864 static int
rl_select_all(int count,int key)865 rl_select_all(int count, int key)
866 {
867 UNUSED(count); UNUSED(key);
868 return run_kb_cmd("s ^");
869 }
870
871 static int
rl_deselect_all(int count,int key)872 rl_deselect_all(int count, int key)
873 {
874 UNUSED(count); UNUSED(key);
875 return run_kb_cmd("ds *");
876 }
877
878 static int
rl_bookmarks(int count,int key)879 rl_bookmarks(int count, int key)
880 {
881 UNUSED(count); UNUSED(key);
882 if (kbind_busy)
883 return EXIT_SUCCESS;
884
885 kbind_busy = 1;
886 keybind_exec_cmd("bm");
887 rl_reset_line_state();
888 return EXIT_SUCCESS;
889 }
890
891 static int
rl_selbox(int count,int key)892 rl_selbox(int count, int key)
893 {
894 UNUSED(count); UNUSED(key);
895 return run_kb_cmd("ds");
896 }
897
898 static int
rl_clear_line(int count,int key)899 rl_clear_line(int count, int key)
900 {
901 UNUSED(count); UNUSED(key);
902 if (kbind_busy && !_xrename)
903 return EXIT_SUCCESS;
904
905 nwords = 0;
906
907 #ifndef _NO_HIGHLIGHT
908 if (cur_color != tx_c) {
909 cur_color = tx_c;
910 fputs(cur_color, stdout);
911 }
912 #endif
913
914 #ifndef _NO_SUGGESTIONS
915 if (wrong_cmd) {
916 if (!recover_from_wrong_cmd())
917 rl_point = 0;
918 }
919 if (suggestion.nlines > term_rows) {
920 rl_on_new_line();
921 return EXIT_SUCCESS;
922 }
923
924 if (suggestion_buf) {
925 clear_suggestion(CS_FREEBUF);
926 suggestion.printed = 0;
927 suggestion.nlines = 0;
928 }
929 #endif
930 curhistindex = current_hist_n;
931 rl_point = 0;
932 rl_delete_text(rl_point, rl_end);
933 rl_end = 0;
934 return EXIT_SUCCESS;
935 }
936
937 static int
rl_sort_next(int count,int key)938 rl_sort_next(int count, int key)
939 {
940 UNUSED(count); UNUSED(key);
941 if (kbind_busy)
942 return EXIT_SUCCESS;
943 #ifndef _NO_SUGGESTIONS
944 if (suggestion.printed && suggestion_buf)
945 free_suggestion();
946 #endif
947 sort++;
948 if (sort > SORT_TYPES)
949 sort = 0;
950
951 if (autols) {
952 if (clear_screen)
953 CLEAR;
954 sort_switch = 1;
955 free_dirlist();
956 putchar('\n');
957 list_dir();
958 sort_switch = 0;
959 }
960
961 rl_reset_line_state();
962 return EXIT_SUCCESS;
963 }
964
965 static int
rl_sort_previous(int count,int key)966 rl_sort_previous(int count, int key)
967 {
968 UNUSED(count); UNUSED(key);
969 if (kbind_busy)
970 return EXIT_SUCCESS;
971 #ifndef _NO_SUGGESTIONS
972 if (suggestion.printed && suggestion_buf)
973 free_suggestion();
974 #endif
975 sort--;
976 if (sort < 0)
977 sort = SORT_TYPES;
978
979 if (autols) {
980 if (clear_screen)
981 CLEAR;
982 sort_switch = 1;
983 free_dirlist();
984 putchar('\n');
985 list_dir();
986 sort_switch = 0;
987 }
988
989 rl_reset_line_state();
990 return EXIT_SUCCESS;
991 }
992
993 static int
rl_lock(int count,int key)994 rl_lock(int count, int key)
995 {
996 UNUSED(count); UNUSED(key);
997 int ret = EXIT_SUCCESS;
998 #ifndef _NO_SUGGESTIONS
999 if (suggestion.printed && suggestion_buf)
1000 free_suggestion();
1001 #endif
1002 rl_deprep_terminal();
1003
1004 #if __FreeBSD__ || __NetBSD__ || __OpenBSD__
1005 char *cmd[] = {"lock", NULL};
1006 #elif __APPLE__
1007 char *cmd[] = {"bashlock", NULL};
1008 #elif __HAIKU__
1009 char *cmd[] = {"peaclock", NULL};
1010 #else
1011 char *cmd[] = {"vlock", NULL};
1012 #endif
1013 ret = launch_execve(cmd, FOREGROUND, E_NOFLAG);
1014
1015 rl_prep_terminal(0);
1016 rl_reset_line_state();
1017
1018 if (ret != EXIT_SUCCESS)
1019 return EXIT_FAILURE;
1020 return EXIT_SUCCESS;
1021 }
1022
1023 static int
rl_remove_sel(int count,int key)1024 rl_remove_sel(int count, int key)
1025 {
1026 UNUSED(count); UNUSED(key);
1027 if (kbind_busy)
1028 return EXIT_SUCCESS;
1029
1030 rl_deprep_terminal();
1031 kb_shortcut = 1;
1032 keybind_exec_cmd("r sel");
1033 kb_shortcut = 0;
1034 rl_prep_terminal(0);
1035 rl_reset_line_state();
1036 return EXIT_SUCCESS;
1037 }
1038
1039 static int
rl_export_sel(int count,int key)1040 rl_export_sel(int count, int key)
1041 {
1042 UNUSED(count); UNUSED(key);
1043 if (kbind_busy)
1044 return EXIT_SUCCESS;
1045
1046 kb_shortcut = 1;
1047 keybind_exec_cmd("exp sel");
1048 kb_shortcut = 0;
1049 rl_reset_line_state();
1050 return EXIT_SUCCESS;
1051 }
1052
1053 static int
rl_move_sel(int count,int key)1054 rl_move_sel(int count, int key)
1055 {
1056 UNUSED(count); UNUSED(key);
1057 if (kbind_busy)
1058 return EXIT_SUCCESS;
1059
1060 kb_shortcut = 1;
1061 keybind_exec_cmd("m sel");
1062 kb_shortcut = 0;
1063 rl_reset_line_state();
1064 return EXIT_SUCCESS;
1065 }
1066
1067 static int
rl_rename_sel(int count,int key)1068 rl_rename_sel(int count, int key)
1069 {
1070 UNUSED(count); UNUSED(key);
1071 if (kbind_busy)
1072 return EXIT_SUCCESS;
1073
1074 kb_shortcut = 1;
1075 keybind_exec_cmd("br sel");
1076 kb_shortcut = 0;
1077 rl_reset_line_state();
1078 return EXIT_SUCCESS;
1079 }
1080
1081 static int
rl_paste_sel(int count,int key)1082 rl_paste_sel(int count, int key)
1083 {
1084 UNUSED(count); UNUSED(key);
1085 if (kbind_busy)
1086 return EXIT_SUCCESS;
1087
1088 kb_shortcut = 1;
1089 rl_deprep_terminal();
1090 keybind_exec_cmd("c sel");
1091 rl_prep_terminal(0);
1092 kb_shortcut = 0;
1093 rl_reset_line_state();
1094 return EXIT_SUCCESS;
1095 }
1096
1097 int
rl_quit(int count,int key)1098 rl_quit(int count, int key)
1099 {
1100 UNUSED(count); UNUSED(key);
1101 if (kbind_busy)
1102 return EXIT_SUCCESS;
1103
1104 /* Reset terminal attributes before exiting. Without this line, the program
1105 * quits, but terminal input is not printed to STDOUT */
1106 tcsetattr(STDIN_FILENO, TCSANOW, &shell_tmodes);
1107 exit(EXIT_SUCCESS);
1108 return EXIT_SUCCESS;
1109 }
1110
1111 /* Get current profile and total amount of profiles and store this info
1112 * in pointers CUR and TOTAL */
1113 static void
get_cur_prof(int * cur,int * total)1114 get_cur_prof(int *cur, int *total)
1115 {
1116 int i;
1117 for (i = 0; profile_names[i]; i++) {
1118 (*total)++;
1119
1120 if (!alt_profile) {
1121 if (*profile_names[i] == 'd'
1122 && strcmp(profile_names[i], "default") == 0)
1123 *cur = i;
1124 } else if (*alt_profile == *profile_names[i]
1125 && strcmp(alt_profile, profile_names[i]) == 0)
1126 *cur = i;
1127 }
1128 }
1129
1130 static int
rl_previous_profile(int count,int key)1131 rl_previous_profile(int count, int key)
1132 {
1133 UNUSED(count); UNUSED(key);
1134 if (kbind_busy)
1135 return EXIT_SUCCESS;
1136 #ifndef _NO_SUGGESTIONS
1137 if (suggestion.printed && suggestion_buf)
1138 free_suggestion();
1139 #endif
1140 int prev_prof, cur_prof = -1, total_profs = 0;
1141 get_cur_prof(&cur_prof, &total_profs);
1142
1143 if (cur_prof == -1 || !profile_names[cur_prof])
1144 return EXIT_FAILURE;
1145
1146 prev_prof = cur_prof - 1;
1147 total_profs--;
1148
1149 if (prev_prof < 0 || !profile_names[prev_prof])
1150 prev_prof = total_profs;
1151
1152 if (clear_screen) {
1153 CLEAR;
1154 } else
1155 putchar('\n');
1156
1157 if (profile_set(profile_names[prev_prof]) == EXIT_SUCCESS) {
1158 char *input = prompt();
1159 free(input);
1160 }
1161
1162 return EXIT_SUCCESS;
1163 }
1164
1165 static int
rl_next_profile(int count,int key)1166 rl_next_profile(int count, int key)
1167 {
1168 UNUSED(count); UNUSED(key);
1169 if (kbind_busy)
1170 return EXIT_SUCCESS;
1171 #ifndef _NO_SUGGESTIONS
1172 if (suggestion.printed && suggestion_buf)
1173 free_suggestion();
1174 #endif
1175 int next_prof, cur_prof = -1, total_profs = 0;
1176 get_cur_prof(&cur_prof, &total_profs);
1177
1178 if (cur_prof == -1 || !profile_names[cur_prof])
1179 return EXIT_FAILURE;
1180
1181 next_prof = cur_prof + 1;
1182 total_profs--;
1183
1184 if (next_prof > (int)total_profs || !profile_names[next_prof])
1185 next_prof = 0;
1186
1187 if (clear_screen) {
1188 CLEAR;
1189 } else
1190 putchar('\n');
1191
1192 if (profile_set(profile_names[next_prof]) == EXIT_SUCCESS) {
1193 char *input = prompt();
1194 free(input);
1195 }
1196
1197 return EXIT_SUCCESS;
1198 }
1199
1200 static int
rl_dirhist(int count,int key)1201 rl_dirhist(int count, int key)
1202 {
1203 UNUSED(count); UNUSED(key);
1204 return run_kb_cmd("bh");
1205 }
1206
1207 static int
rl_archive_sel(int count,int key)1208 rl_archive_sel(int count, int key)
1209 {
1210 UNUSED(count); UNUSED(key);
1211 return run_kb_cmd("ac sel");
1212 }
1213
1214 static int
rl_new_instance(int count,int key)1215 rl_new_instance(int count, int key)
1216 {
1217 UNUSED(count); UNUSED(key);
1218 return run_kb_cmd("x .");
1219 }
1220
1221 static int
rl_clear_msgs(int count,int key)1222 rl_clear_msgs(int count, int key)
1223 {
1224 UNUSED(count); UNUSED(key);
1225 return run_kb_cmd("msg clear");
1226 }
1227
1228 static int
rl_trash_sel(int count,int key)1229 rl_trash_sel(int count, int key)
1230 {
1231 UNUSED(count); UNUSED(key);
1232 return run_kb_cmd("t sel");
1233 }
1234
1235 static int
rl_untrash_all(int count,int key)1236 rl_untrash_all(int count, int key)
1237 {
1238 UNUSED(count); UNUSED(key);
1239 return run_kb_cmd("u *");
1240 }
1241
1242 static int
rl_open_sel(int count,int key)1243 rl_open_sel(int count, int key)
1244 {
1245 UNUSED(count); UNUSED(key);
1246 if (kbind_busy)
1247 return EXIT_SUCCESS;
1248
1249 char cmd[PATH_MAX + 3];
1250 sprintf(cmd, "o %s", (sel_n && sel_elements[sel_n - 1])
1251 ? sel_elements[sel_n - 1] : "sel");
1252
1253 kb_shortcut = 1;
1254 keybind_exec_cmd(cmd);
1255 kb_shortcut = 0;
1256 rl_reset_line_state();
1257 return EXIT_SUCCESS;
1258 }
1259
1260 static int
rl_bm_sel(int count,int key)1261 rl_bm_sel(int count, int key)
1262 {
1263 UNUSED(count); UNUSED(key);
1264 if (kbind_busy)
1265 return EXIT_SUCCESS;
1266
1267 char cmd[PATH_MAX + 6];
1268 sprintf(cmd, "bm a %s", (sel_n && sel_elements[sel_n - 1] )
1269 ? sel_elements[sel_n - 1] : "sel");
1270
1271 kb_shortcut = 1;
1272 keybind_exec_cmd(cmd);
1273 kb_shortcut = 0;
1274 rl_reset_line_state();
1275 return EXIT_SUCCESS;
1276 }
1277
1278 static int
run_man_cmd(char * str)1279 run_man_cmd(char *str)
1280 {
1281 char *mp = (char *)NULL;
1282 char *p = getenv("MANPAGER");
1283 if (p) {
1284 mp = (char *)xnmalloc(strlen(p) + 1, sizeof(char *));
1285 strcpy(mp, p);
1286 unsetenv("MANPAGER");
1287 }
1288
1289 int ret = launch_execle(str) != EXIT_SUCCESS;
1290
1291 if (mp) {
1292 setenv("MANPAGER", mp, 1);
1293 free(mp);
1294 }
1295
1296 return ret;
1297 }
1298
1299 static int
rl_kbinds_help(int count,int key)1300 rl_kbinds_help(int count, int key)
1301 {
1302 UNUSED(count); UNUSED(key);
1303 #ifndef _NO_SUGGESTIONS
1304 if (suggestion.printed && suggestion_buf)
1305 free_suggestion();
1306 #endif
1307
1308 char cmd[PATH_MAX];
1309 snprintf(cmd, PATH_MAX - 1,
1310 "export PAGER=\"less -p ^[0-9]+\\.[[:space:]]KEYBOARD[[:space:]]SHORTCUTS\"; man %s\n",
1311 PNL);
1312 int ret = run_man_cmd(cmd);
1313 if (!ret)
1314 return EXIT_FAILURE;
1315 return EXIT_SUCCESS;
1316 }
1317
1318 static int
rl_cmds_help(int count,int key)1319 rl_cmds_help(int count, int key)
1320 {
1321 UNUSED(count); UNUSED(key);
1322 #ifndef _NO_SUGGESTIONS
1323 if (suggestion.printed && suggestion_buf)
1324 free_suggestion();
1325 #endif
1326
1327 char cmd[PATH_MAX];
1328 snprintf(cmd, PATH_MAX - 1,
1329 "export PAGER=\"less -p ^[0-9]+\\.[[:space:]]COMMANDS\"; man %s\n",
1330 PNL);
1331 int ret = run_man_cmd(cmd);
1332
1333 if (!ret)
1334 return EXIT_FAILURE;
1335 return EXIT_SUCCESS;
1336 }
1337
1338 static int
rl_manpage(int count,int key)1339 rl_manpage(int count, int key)
1340 {
1341 UNUSED(count); UNUSED(key);
1342 #ifndef _NO_SUGGESTIONS
1343 if (suggestion.printed && suggestion_buf)
1344 free_suggestion();
1345 #endif
1346 char *cmd[] = {"man", PNL, NULL};
1347 if (launch_execve(cmd, FOREGROUND, E_NOFLAG) != EXIT_SUCCESS)
1348 return EXIT_FAILURE;
1349 return EXIT_SUCCESS;
1350 }
1351
1352 static int
rl_pinned_dir(int count,int key)1353 rl_pinned_dir(int count, int key)
1354 {
1355 UNUSED(count); UNUSED(key);
1356 if (!pinned_dir) {
1357 printf(_("%s: No pinned file\n"), PROGRAM_NAME);
1358 rl_reset_line_state();
1359 return EXIT_SUCCESS;
1360 }
1361
1362 return run_kb_cmd(",");
1363 }
1364
1365 static int
rl_ws1(int count,int key)1366 rl_ws1(int count, int key)
1367 {
1368 UNUSED(count); UNUSED(key);
1369 if (cur_ws == 0)
1370 return EXIT_SUCCESS;
1371 return run_kb_cmd("ws 1");
1372 }
1373
1374 static int
rl_ws2(int count,int key)1375 rl_ws2(int count, int key)
1376 {
1377 UNUSED(count); UNUSED(key);
1378 if (cur_ws == 1)
1379 return EXIT_SUCCESS;
1380 return run_kb_cmd("ws 2");
1381 }
1382
1383 static int
rl_ws3(int count,int key)1384 rl_ws3(int count, int key)
1385 {
1386 UNUSED(count); UNUSED(key);
1387 if (cur_ws == 2)
1388 return EXIT_SUCCESS;
1389 return run_kb_cmd("ws 3");
1390 }
1391
1392 static int
rl_ws4(int count,int key)1393 rl_ws4(int count, int key)
1394 {
1395 UNUSED(count); UNUSED(key);
1396 if (cur_ws == 3)
1397 return EXIT_SUCCESS;
1398 return run_kb_cmd("ws 4");
1399 }
1400
1401 static int
rl_plugin1(int count,int key)1402 rl_plugin1(int count, int key)
1403 {
1404 UNUSED(count); UNUSED(key);
1405 return run_kb_cmd("plugin1");
1406 }
1407
1408 static int
rl_plugin2(int count,int key)1409 rl_plugin2(int count, int key)
1410 {
1411 UNUSED(count); UNUSED(key);
1412 return run_kb_cmd("plugin2");
1413 }
1414
1415 static int
rl_plugin3(int count,int key)1416 rl_plugin3(int count, int key)
1417 {
1418 UNUSED(count); UNUSED(key);
1419 return run_kb_cmd("plugin3");
1420 }
1421
1422 static int
rl_plugin4(int count,int key)1423 rl_plugin4(int count, int key)
1424 {
1425 UNUSED(count); UNUSED(key);
1426 return run_kb_cmd("plugin4");
1427 }
1428
1429 static int
rl_onlydirs(int count,int key)1430 rl_onlydirs(int count, int key)
1431 {
1432 UNUSED(count); UNUSED(key);
1433
1434 if (kbind_busy)
1435 return EXIT_SUCCESS;
1436
1437 only_dirs = only_dirs ? 0 : 1;
1438
1439 int exit_status = EXIT_SUCCESS;
1440 if (autols) {
1441 if (clear_screen)
1442 CLEAR;
1443 free_dirlist();
1444 putchar('\n');
1445 exit_status = list_dir();
1446 } else {
1447 printf(_("%s: List only directories set to %s\n"),
1448 PROGRAM_NAME, only_dirs ? _("true"): _("false"));
1449 }
1450
1451 rl_reset_line_state();
1452 return exit_status;
1453 }
1454
1455 #ifndef _NO_HIGHLIGHT
1456 static void
print_highlight_string(char * s)1457 print_highlight_string(char *s)
1458 {
1459 if (!s || !*s)
1460 return;
1461
1462 size_t i, l = 0;
1463 rl_delete_text(0, rl_end);
1464 rl_point = rl_end = 0;
1465 fputs(tx_c, stdout);
1466 char q[PATH_MAX];
1467 for (i = 0; s[i]; i++) {
1468 rl_highlight(s, i, SET_COLOR);
1469 if (s[i] < 0) {
1470 q[l++] = s[i];
1471 if (s[i + 1] >= 0) {
1472 q[l] = '\0';
1473 l = 0;
1474 rl_insert_text(q);
1475 rl_redisplay();
1476 }
1477 continue;
1478 }
1479 q[0] = s[i];
1480 q[1] = '\0';
1481 rl_insert_text(q);
1482 rl_redisplay();
1483 }
1484 }
1485 #endif
1486
1487 static int
rl_cmdhist(int count,int key)1488 rl_cmdhist(int count, int key)
1489 {
1490 UNUSED(count);
1491 if (rl_nohist)
1492 return EXIT_SUCCESS;
1493
1494 #ifndef _NO_SUGGESTIONS
1495 if (suggestion_buf) {
1496 free(suggestion_buf);
1497 suggestion_buf = (char *)NULL;
1498 }
1499 #endif
1500
1501 int p = (int)curhistindex;
1502
1503 /* If the cursor is at the beginning of the line */
1504 if (rl_point == 0 || cmdhist_flag == 1) {
1505 cmdhist_flag = 1;
1506 if (key == 65) {
1507 if (--p < 0)
1508 return EXIT_FAILURE;
1509 } else if (key == 66) {
1510 if (rl_end == 0)
1511 return EXIT_SUCCESS;
1512 if (++p >= (int)current_hist_n) {
1513 rl_replace_line("", 1);
1514 curhistindex++;
1515 return EXIT_SUCCESS;
1516 }
1517 } else {
1518 return EXIT_FAILURE;
1519 }
1520
1521 if (!history[p])
1522 return EXIT_FAILURE;
1523
1524 curhistindex = (size_t)p;
1525
1526 fputs("\x1b[?25l", stdout);
1527 #ifndef _NO_HIGHLIGHT
1528 if (highlight)
1529 print_highlight_string(history[p]);
1530 else
1531 #endif
1532 {
1533 rl_replace_line(history[p], 1);
1534 }
1535
1536 fputs("\x1b[?25h", stdout);
1537 rl_point = rl_end;
1538 cur_color = df_c;
1539 fputs(df_c, stdout);
1540 return EXIT_SUCCESS;
1541 }
1542
1543 /* If cursor is not at the beginning of the line */
1544 int found = 0;
1545 if (key == 65) {
1546 if (--p < 0)
1547 return EXIT_FAILURE;
1548
1549 while (p >= 0 && history[p]) {
1550 if (strncmp(rl_line_buffer, history[p], (size_t)rl_point) == 0
1551 && strcmp(rl_line_buffer, history[p]) != 0) {
1552 found = 1;
1553 break;
1554 }
1555 p--;
1556 }
1557 } else if (key == 66) {
1558 if (++p >= (int)current_hist_n)
1559 return EXIT_FAILURE;
1560
1561 while (history[p]) {
1562 if (strncmp(rl_line_buffer, history[p], (size_t)rl_point) == 0
1563 && strcmp(rl_line_buffer, history[p]) != 0) {
1564 found = 1;
1565 break;
1566 }
1567 p++;
1568 }
1569 } else {
1570 return EXIT_FAILURE;
1571 }
1572
1573 if (!found) {
1574 rl_ring_bell();
1575 return EXIT_FAILURE;
1576 }
1577
1578 fputs("\x1b[?25l", stdout);
1579 curhistindex = (size_t)p;
1580 int bk = rl_point;
1581 #ifndef _NO_HIGHLIGHT
1582 if (highlight)
1583 print_highlight_string(history[p]);
1584 else
1585 #endif
1586 {
1587 rl_replace_line(history[p], 1);
1588 }
1589
1590 fputs("\x1b[?25h", stdout);
1591 rl_point = bk;
1592 cur_color = tx_c;
1593 fputs(tx_c, stdout);
1594 return EXIT_SUCCESS;
1595 }
1596
1597 static int
rl_tab_comp(int count,int key)1598 rl_tab_comp(int count, int key)
1599 {
1600 #ifndef _NO_SUGGESTIONS
1601 if (suggestion.printed && suggestion_buf)
1602 clear_suggestion(CS_FREEBUF);
1603 #endif
1604 UNUSED(count); UNUSED(key);
1605
1606 tab_complete('!');
1607 return EXIT_SUCCESS;
1608 }
1609
1610 static int
rl_del_last_word(int count,int key)1611 rl_del_last_word(int count, int key)
1612 {
1613 #ifndef _NO_SUGGESTIONS
1614 if (suggestion.printed && suggestion_buf)
1615 clear_suggestion(CS_FREEBUF);
1616 #endif
1617 UNUSED(count); UNUSED(key);
1618
1619 if (rl_point == 0)
1620 return EXIT_SUCCESS;
1621
1622 char *b = rl_line_buffer;
1623
1624 if (b[rl_point - 1] == '/' || b[rl_point - 1] == ' ') {
1625 --rl_point;
1626 b[rl_point] = '\0';
1627 --rl_end;
1628 }
1629
1630 int p = 0;
1631 char *s = strrchr(b, '/');
1632 char *a = strrchr(b, ' ');
1633 if (!a) {
1634 if (s)
1635 p = (int)(s - b) + 1;
1636 } else if (!s)
1637 p = (int)(a - b) + 1;
1638 else if (a > s)
1639 p = (int)(a - b) + 1;
1640 else
1641 p = (int)(s - b) + 1;
1642
1643 rl_delete_text(p, rl_end);
1644 rl_point = rl_end = p;
1645 rl_redisplay();
1646
1647 return EXIT_SUCCESS;
1648 }
1649
1650
1651 /*
1652 void
1653 add_func_to_rl(void)
1654 {
1655 rl_add_defun("my-test", rl_test, -1);
1656 } */
1657
1658 /* To get the keyseq value for a given key do this in an Xterm terminal:
1659 * C-v and then press the key (or the key combination). So, for example,
1660 * C-v, C-right arrow gives "[[1;5C", which here should be written like
1661 * this:
1662 * "\\x1b[1;5C" */
1663 void
readline_kbinds(void)1664 readline_kbinds(void)
1665 {
1666
1667 /* ##############################
1668 * # KEYBINDINGS #
1669 * ##############################*/
1670
1671 if (kbinds_file) {
1672 /* Help */
1673 rl_bind_keyseq(find_key("show-manpage"), rl_manpage);
1674 rl_bind_keyseq(find_key("show-manpage2"), rl_manpage);
1675 rl_bind_keyseq(find_key("show-cmds"), rl_cmds_help);
1676 rl_bind_keyseq(find_key("show-cmds2"), rl_cmds_help);
1677 rl_bind_keyseq(find_key("show-kbinds"), rl_kbinds_help);
1678 rl_bind_keyseq(find_key("show-kbinds2"), rl_kbinds_help);
1679
1680 /* Navigation */
1681 /* Define multiple keybinds for different terminals:
1682 * rxvt, xterm, kernel console */
1683 /* rl_bind_keyseq("\\M-[D", rl_test); // Left arrow key
1684 rl_bind_keyseq("\\M-+", rl_test); */
1685 rl_bind_keyseq(find_key("parent-dir"), rl_parent_dir);
1686 rl_bind_keyseq(find_key("parent-dir2"), rl_parent_dir);
1687 rl_bind_keyseq(find_key("parent-dir3"), rl_parent_dir);
1688 rl_bind_keyseq(find_key("parent-dir4"), rl_parent_dir);
1689 rl_bind_keyseq(find_key("previous-dir"), rl_previous_dir);
1690 rl_bind_keyseq(find_key("previous-dir2"), rl_previous_dir);
1691 rl_bind_keyseq(find_key("previous-dir3"), rl_previous_dir);
1692 rl_bind_keyseq(find_key("previous-dir4"), rl_previous_dir);
1693 rl_bind_keyseq(find_key("next-dir"), rl_next_dir);
1694 rl_bind_keyseq(find_key("next-dir2"), rl_next_dir);
1695 rl_bind_keyseq(find_key("next-dir3"), rl_next_dir);
1696 rl_bind_keyseq(find_key("next-dir4"), rl_next_dir);
1697 rl_bind_keyseq(find_key("home-dir"), rl_home_dir);
1698 rl_bind_keyseq(find_key("home-dir2"), rl_home_dir);
1699 rl_bind_keyseq(find_key("home-dir3"), rl_home_dir);
1700 rl_bind_keyseq(find_key("home-dir4"), rl_home_dir);
1701 rl_bind_keyseq(find_key("root-dir"), rl_root_dir);
1702 rl_bind_keyseq(find_key("root-dir2"), rl_root_dir);
1703 rl_bind_keyseq(find_key("root-dir3"), rl_root_dir);
1704 rl_bind_keyseq(find_key("first-dir"), rl_first_dir);
1705 rl_bind_keyseq(find_key("last-dir"), rl_last_dir);
1706 rl_bind_keyseq(find_key("pinned-dir"), rl_pinned_dir);
1707 rl_bind_keyseq(find_key("workspace1"), rl_ws1);
1708 rl_bind_keyseq(find_key("workspace2"), rl_ws2);
1709 rl_bind_keyseq(find_key("workspace3"), rl_ws3);
1710 rl_bind_keyseq(find_key("workspace4"), rl_ws4);
1711
1712 /* Operations on files */
1713 rl_bind_keyseq(find_key("create-file"), rl_create_file);
1714 rl_bind_keyseq(find_key("bookmark-sel"), rl_bm_sel);
1715 rl_bind_keyseq(find_key("archive-sel"), rl_archive_sel);
1716 rl_bind_keyseq(find_key("open-sel"), rl_open_sel);
1717 rl_bind_keyseq(find_key("export-sel"), rl_export_sel);
1718 rl_bind_keyseq(find_key("move-sel"), rl_move_sel);
1719 rl_bind_keyseq(find_key("rename-sel"), rl_rename_sel);
1720 rl_bind_keyseq(find_key("remove-sel"), rl_remove_sel);
1721 rl_bind_keyseq(find_key("trash-sel"), rl_trash_sel);
1722 rl_bind_keyseq(find_key("untrash-all"), rl_untrash_all);
1723 rl_bind_keyseq(find_key("paste-sel"), rl_paste_sel);
1724 rl_bind_keyseq(find_key("select-all"), rl_select_all);
1725 rl_bind_keyseq(find_key("deselect-all"), rl_deselect_all);
1726
1727 /* Config files */
1728 rl_bind_keyseq(find_key("open-mime"), rl_open_mime);
1729 rl_bind_keyseq(find_key("open-jump-db"), rl_open_jump_db);
1730 rl_bind_keyseq(find_key("edit-color-scheme"), rl_open_cscheme);
1731 rl_bind_keyseq(find_key("open-config"), rl_open_config);
1732 rl_bind_keyseq(find_key("open-keybinds"), rl_open_keybinds);
1733 rl_bind_keyseq(find_key("open-bookmarks"), rl_open_bm_file);
1734
1735 /* Settings */
1736 rl_bind_keyseq(find_key("clear-msgs"), rl_clear_msgs);
1737 rl_bind_keyseq(find_key("next-profile"), rl_next_profile);
1738 rl_bind_keyseq(find_key("previous-profile"), rl_previous_profile);
1739 rl_bind_keyseq(find_key("quit"), rl_quit);
1740 rl_bind_keyseq(find_key("lock"), rl_lock);
1741 rl_bind_keyseq(find_key("refresh-screen"), rl_refresh);
1742 rl_bind_keyseq(find_key("clear-line"), rl_clear_line);
1743 rl_bind_keyseq(find_key("toggle-hidden"), rl_hidden);
1744 rl_bind_keyseq(find_key("toggle-hidden2"), rl_hidden);
1745 rl_bind_keyseq(find_key("toggle-long"), rl_long);
1746 rl_bind_keyseq(find_key("toggle-light"), rl_light);
1747 rl_bind_keyseq(find_key("folders-first"), rl_folders_first);
1748 rl_bind_keyseq(find_key("sort-previous"), rl_sort_previous);
1749 rl_bind_keyseq(find_key("sort-next"), rl_sort_next);
1750 rl_bind_keyseq(find_key("only-dirs"), rl_onlydirs);
1751
1752 rl_bind_keyseq(find_key("new-instance"), rl_new_instance);
1753 rl_bind_keyseq(find_key("show-dirhist"), rl_dirhist);
1754 rl_bind_keyseq(find_key("bookmarks"), rl_bookmarks);
1755 rl_bind_keyseq(find_key("mountpoints"), rl_mountpoints);
1756 rl_bind_keyseq(find_key("selbox"), rl_selbox);
1757 rl_bind_keyseq(find_key("prepend-sudo"), rl_prepend_sudo);
1758
1759 /* Plugins */
1760 rl_bind_keyseq(find_key("plugin1"), rl_plugin1);
1761 rl_bind_keyseq(find_key("plugin2"), rl_plugin2);
1762 rl_bind_keyseq(find_key("plugin3"), rl_plugin3);
1763 rl_bind_keyseq(find_key("plugin4"), rl_plugin4);
1764
1765 rl_bind_keyseq(find_key("quit"), rl_quit);
1766 }
1767
1768 /* If no kbinds file is found, set the defaults */
1769 else {
1770 /* Help */
1771 rl_bind_keyseq("\\eOP", rl_manpage);
1772 rl_bind_keyseq("\\eOQ", rl_cmds_help);
1773 rl_bind_keyseq("\\eOR", rl_kbinds_help);
1774 rl_bind_keyseq("\\e[11~", rl_manpage);
1775 rl_bind_keyseq("\\e[12~", rl_cmds_help);
1776 rl_bind_keyseq("\\e[13~", rl_kbinds_help);
1777
1778 /* Navigation */
1779 rl_bind_keyseq("\\M-u", rl_parent_dir);
1780 rl_bind_keyseq("\\e[a", rl_parent_dir);
1781 rl_bind_keyseq("\\e[2A", rl_parent_dir);
1782 rl_bind_keyseq("\\e[1;2A", rl_parent_dir);
1783 rl_bind_keyseq("\\M-j", rl_previous_dir);
1784 rl_bind_keyseq("\\e[d", rl_previous_dir);
1785 rl_bind_keyseq("\\e[2D", rl_previous_dir);
1786 rl_bind_keyseq("\\e[1;2D", rl_previous_dir);
1787 rl_bind_keyseq("\\M-k", rl_next_dir);
1788 rl_bind_keyseq("\\e[c", rl_next_dir);
1789 rl_bind_keyseq("\\e[2C", rl_next_dir);
1790 rl_bind_keyseq("\\e[1;2C", rl_next_dir);
1791 rl_bind_keyseq("\\M-e", rl_home_dir);
1792 rl_bind_keyseq("\\e[1~", rl_home_dir);
1793 rl_bind_keyseq("\\e[7~", rl_home_dir);
1794 rl_bind_keyseq("\\e[H", rl_home_dir);
1795 rl_bind_keyseq("\\M-r", rl_root_dir);
1796 rl_bind_keyseq("\\e/", rl_root_dir);
1797 rl_bind_keyseq("\\C-\\M-j", rl_first_dir);
1798 rl_bind_keyseq("\\C-\\M-k", rl_last_dir);
1799 rl_bind_keyseq("\\M-p", rl_pinned_dir);
1800 rl_bind_keyseq("\\M-1", rl_ws1);
1801 rl_bind_keyseq("\\M-2", rl_ws2);
1802 rl_bind_keyseq("\\M-3", rl_ws3);
1803 rl_bind_keyseq("\\M-4", rl_ws4);
1804
1805 /* Operations on files */
1806 rl_bind_keyseq("\\M-n", rl_create_file);
1807 rl_bind_keyseq("\\C-\\M-b", rl_bm_sel);
1808 rl_bind_keyseq("\\C-\\M-a", rl_archive_sel);
1809 rl_bind_keyseq("\\C-\\M-g", rl_open_sel);
1810 rl_bind_keyseq("\\C-\\M-e", rl_export_sel);
1811 rl_bind_keyseq("\\C-\\M-n", rl_move_sel);
1812 rl_bind_keyseq("\\C-\\M-r", rl_rename_sel);
1813 rl_bind_keyseq("\\C-\\M-d", rl_remove_sel);
1814 rl_bind_keyseq("\\C-\\M-t", rl_trash_sel);
1815 rl_bind_keyseq("\\C-\\M-u", rl_untrash_all);
1816 rl_bind_keyseq("\\C-\\M-v", rl_paste_sel);
1817 rl_bind_keyseq("\\M-a", rl_select_all);
1818 rl_bind_keyseq("\\M-d", rl_deselect_all);
1819 rl_bind_keyseq("\\M-v", rl_prepend_sudo);
1820
1821 /* Config files */
1822 rl_bind_keyseq("\\e[17~", rl_open_mime);
1823 rl_bind_keyseq("\\e[18~", rl_open_jump_db);
1824 rl_bind_keyseq("\\e[19~", rl_open_cscheme);
1825 rl_bind_keyseq("\\e[20~", rl_open_keybinds);
1826 rl_bind_keyseq("\\e[21~", rl_open_config);
1827 rl_bind_keyseq("\\e[23~", rl_open_bm_file);
1828
1829 /* Settings */
1830 rl_bind_keyseq("\\M-t", rl_clear_msgs);
1831 /* rl_bind_keyseq("", rl_next_profile);
1832 rl_bind_keyseq("", rl_previous_profile); */
1833 rl_bind_keyseq("\\M-o", rl_lock);
1834 rl_bind_keyseq("\\C-r", rl_refresh);
1835 rl_bind_keyseq("\\M-c", rl_clear_line);
1836 rl_bind_keyseq("\\M-i", rl_hidden);
1837 rl_bind_keyseq("\\M-.", rl_hidden);
1838 rl_bind_keyseq("\\M-l", rl_long);
1839 rl_bind_keyseq("\\M-y", rl_light);
1840 rl_bind_keyseq("\\M-g", rl_folders_first);
1841 rl_bind_keyseq("\\M-z", rl_sort_previous);
1842 rl_bind_keyseq("\\M-x", rl_sort_next);
1843 rl_bind_keyseq("\\M-,", rl_onlydirs);
1844
1845 rl_bind_keyseq("\\C-x", rl_new_instance);
1846 rl_bind_keyseq("\\M-h", rl_dirhist);
1847 rl_bind_keyseq("\\M-b", rl_bookmarks);
1848 rl_bind_keyseq("\\M-m", rl_mountpoints);
1849 rl_bind_keyseq("\\M-s", rl_selbox);
1850
1851 rl_bind_keyseq("\\e[24~", rl_quit);
1852 }
1853
1854 rl_bind_keyseq("\x1b[A", rl_cmdhist);
1855 rl_bind_keyseq("\x1b[B", rl_cmdhist);
1856
1857 rl_bind_keyseq("\\M-q", rl_del_last_word);
1858 rl_bind_key('\t', rl_tab_comp);
1859 /* char *term = getenv("TERM");
1860 tgetent(NULL, term);
1861 char *_right_arrow = tgetstr("nd", NULL);
1862 char *s_right_arrow = tgetstr("%i", NULL);
1863 char *s_left_arrow = tgetstr("#4", NULL); */
1864
1865 /* Bind Right arrow key and Ctrl-f to accept the whole suggestion */
1866 /* rl_bind_keyseq(_right_arrow, rl_accept_suggestion);
1867 rl_bind_key(6, rl_accept_suggestion);
1868 rl_bind_keyseq(s_left_arrow, rl_previous_dir);
1869 rl_bind_keyseq(s_right_arrow, rl_next_dir); */
1870
1871 #ifndef _NO_SUGGESTIONS
1872 #ifndef __HAIKU__
1873 rl_bind_keyseq("\\C-f", rl_accept_suggestion);
1874 rl_bind_keyseq("\x1b[C", rl_accept_suggestion);
1875 rl_bind_keyseq("\x1bOC", rl_accept_suggestion);
1876
1877 /* Bind Alt-Right and Alt-f to accept the first suggested word */
1878 /* rl_bind_key(('f' | 0200), rl_accept_first_word); */ // Alt-f
1879 rl_bind_keyseq("\x1b\x66", rl_accept_first_word);
1880 rl_bind_keyseq("\x1b[3C", rl_accept_first_word);
1881 rl_bind_keyseq("\x1b\x1b[C", rl_accept_first_word);
1882 rl_bind_keyseq("\x1b[1;3C", rl_accept_first_word);
1883 #else
1884 rl_bind_keyseq("\x1bOC", rl_accept_suggestion);
1885 rl_bind_keyseq("\\C-f", rl_accept_first_word);
1886 #endif /* __HAIKU__ */
1887 #endif /* !_NO_SUGGESTIONS */
1888 }
1889