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