1 /* exec.c -- functions controlling the execution of programs */
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 #ifdef __OpenBSD__
27 #include <sys/dirent.h>
28 #endif
29 #include <dirent.h>
30 #include <errno.h>
31 #include <fcntl.h>
32 #include <stdio.h>
33 #include <string.h>
34 #include <sys/stat.h>
35 #include <sys/wait.h>
36 #include <unistd.h>
37 #include <readline/readline.h>
38 #include <limits.h>
39 
40 #include "actions.h"
41 #ifndef _NO_ARCHIVING
42 #include "archives.h"
43 #endif
44 #include "aux.h"
45 #include "bookmarks.h"
46 #include "checks.h"
47 #include "colors.h"
48 #include "config.h"
49 #include "exec.h"
50 #include "file_operations.h"
51 #include "history.h"
52 #include "init.h"
53 #include "jump.h"
54 #include "keybinds.h"
55 #include "listing.h"
56 #include "mime.h"
57 #include "misc.h"
58 #include "navigation.h"
59 #include "profiles.h"
60 #include "properties.h"
61 #include "readline.h"
62 #include "remotes.h"
63 #include "search.h"
64 #include "selection.h"
65 #include "sort.h"
66 #include "strings.h"
67 #ifndef _NO_TRASH
68 #include "trash.h"
69 #endif
70 #include "messages.h"
71 #include "media.h"
72 #ifndef _NO_BLEACH
73 #include "name_cleaner.h"
74 #endif
75 
76 char **_comm = (char **)NULL;
77 
78 static char *
get_new_name(void)79 get_new_name(void)
80 {
81 	char *input = (char *)NULL;
82 
83 	rl_nohist = 1;
84 
85 	char m[NAME_MAX];
86 	sprintf(m, "Enter new name ('q' to quit)\n%s>%s ", mi_c, tx_c);
87 	while (!input && _xrename) {
88 		input = readline(m);
89 		if (!input)
90 			continue;
91 		if (!*input || *input == ' ') {
92 			free(input);
93 			input = (char *)NULL;
94 			continue;
95 		}
96 
97 		if (*input == 'q' && !*(input + 1)) {
98 			free(input);
99 			input = (char *)NULL;
100 			break;
101 		}
102 	}
103 
104 	rl_nohist = 0;
105 	return input;
106 }
107 
108 /* Run a command via execle() and refresh the screen in case of success */
109 int
run_and_refresh(char ** cmd)110 run_and_refresh(char **cmd)
111 {
112 	if (!cmd)
113 		return EXIT_FAILURE;
114 
115 	log_function(cmd);
116 
117 	size_t i = 0, total_len = 0;
118 	for (; i <= args_n; i++)
119 		total_len += strlen(cmd[i]);
120 
121 	char *tmp_cmd = (char *)NULL;
122 	tmp_cmd = (char *)xcalloc(total_len + (i + 1) + 1, sizeof(char));
123 
124 	for (i = 0; i <= args_n; i++) {
125 		strcat(tmp_cmd, cmd[i]);
126 		strcat(tmp_cmd, " ");
127 	}
128 
129 	if (xrename) {
130 		/* If we have a number here, it was not expanded by parse_input_str,
131 		 * and thereby, we have an invalid ELN */
132 		if (is_number(cmd[1])) {
133 			fprintf(stderr, "%s: %s: Invalid ELN\n", PROGRAM_NAME, cmd[1]);
134 			free(tmp_cmd);
135 			xrename = 0;
136 			return EXIT_FAILURE;
137 		}
138 		_xrename = 1;
139 		char *new_name = get_new_name();
140 		_xrename = 0;
141 		if (!new_name) {
142 			free(tmp_cmd);
143 			return EXIT_SUCCESS;
144 		}
145 		char *enn = (char *)NULL;
146 		if (!strchr(new_name, '\\')) {
147 			enn = escape_str(new_name);
148 			if (!enn) {
149 				free(tmp_cmd);
150 				fprintf(stderr, "%s: %s: Error escaping string\n", PROGRAM_NAME, new_name);
151 				return EXIT_FAILURE;
152 			}
153 		}
154 		tmp_cmd = (char *)xrealloc(tmp_cmd,
155 				(total_len + (i + 1) + 1 + strlen(enn ? enn : new_name))
156 				* sizeof(char));
157 		strcat(tmp_cmd, enn ? enn : new_name);
158 
159 		free(new_name);
160 		free(enn);
161 	}
162 
163 	int ret = launch_execle(tmp_cmd);
164 	free(tmp_cmd);
165 
166 	if (ret != EXIT_SUCCESS)
167 		return EXIT_FAILURE;
168 	/* Error messages will be printed by launch_execve() itself */
169 
170 	/* If 'rm sel' and command is successful, deselect everything */
171 	if (is_sel && *cmd[0] == 'r' && cmd[0][1] == 'm' && (!cmd[0][2]
172 	|| cmd[0][2] == ' ')) {
173 		int j = (int)sel_n;
174 		while (--j >= 0)
175 			free(sel_elements[j]);
176 		sel_n = 0;
177 		save_sel();
178 	}
179 
180 #ifdef __HAIKU__
181 	if (autols && cmd[1] && strcmp(cmd[1], "--help") != 0
182 	&& strcmp(cmd[1], "--version") != 0) {
183 		free_dirlist();
184 		list_dir();
185 	}
186 #endif
187 
188 	return EXIT_SUCCESS;
189 }
190 
191 static int
run_in_foreground(pid_t pid)192 run_in_foreground(pid_t pid)
193 {
194 	int status = 0;
195 
196 	/* The parent process calls waitpid() on the child */
197 	if (waitpid(pid, &status, 0) > 0) {
198 		if (WIFEXITED(status) && !WEXITSTATUS(status)) {
199 			/* The program terminated normally and executed successfully
200 			 * (WEXITSTATUS(status) == 0) */
201 			return EXIT_SUCCESS;
202 		} else if (WIFEXITED(status) && WEXITSTATUS(status)) {
203 			/* Program terminated normally, but returned a
204 			 * non-zero status. Error codes should be printed by the
205 			 * program itself */
206 			return WEXITSTATUS(status);
207 		} else {
208 			/* The program didn't terminate normally. In this case too,
209 			 * error codes should be printed by the program */
210 			return EXCRASHERR;
211 		}
212 	} else {
213 		/* waitpid() failed */
214 		fprintf(stderr, "%s: waitpid: %s\n", PROGRAM_NAME,
215 		    strerror(errno));
216 		return errno;
217 	}
218 
219 	return EXIT_FAILURE; /* Never reached */
220 }
221 
222 static void
run_in_background(pid_t pid)223 run_in_background(pid_t pid)
224 {
225 	int status = 0;
226 	/* Keep it in the background */
227 	waitpid(pid, &status, WNOHANG); /* or: kill(pid, SIGCONT); */
228 }
229 
230 /* Execute a command using the system shell (/bin/sh), which takes care
231  * of special functions such as pipes and stream redirection, and special
232  * chars like wildcards, quotes, and escape sequences. Use only when the
233  * shell is needed; otherwise, launch_execve() should be used instead. */
234 int
launch_execle(const char * cmd)235 launch_execle(const char *cmd)
236 {
237 	if (!cmd || !*cmd)
238 		return EXNULLERR;
239 
240 	int ret = system(cmd);
241 	if (WIFEXITED(ret) && !WEXITSTATUS(ret))
242 		return EXIT_SUCCESS;
243 	if (WIFEXITED(ret) && WEXITSTATUS(ret))
244 		return WEXITSTATUS(ret);
245 	return EXCRASHERR;
246 /*
247 	// Reenable SIGCHLD, in case it was disabled. Otherwise, waitpid won't
248 	// be able to catch error codes coming from the child
249 	signal(SIGCHLD, SIG_DFL);
250 
251 	int status;
252 	pid_t pid = fork();
253 	if (pid < 0) {
254 		fprintf(stderr, "%s: fork: %s\n", PROGRAM_NAME, strerror(errno));
255 		return EXFORKERR;
256 	} else if (pid == 0) {
257 		// Reenable signals only for the child, in case they were
258 		// disabled for the parent
259 		signal(SIGHUP, SIG_DFL);
260 		signal(SIGINT, SIG_DFL);
261 		signal(SIGQUIT, SIG_DFL);
262 		signal(SIGTERM, SIG_DFL);
263 
264 		// Get shell base name
265 		char *name = strrchr(user.shell, '/');
266 
267 		execl(user.shell, name ? name + 1 : user.shell, "-c", cmd, NULL);
268 		fprintf(stderr, "%s: %s: execle: %s\n", PROGRAM_NAME, user.shell,
269 		    strerror(errno));
270 		_exit(errno);
271 	}
272 	// Get command status
273 	else {
274 		// The parent process calls waitpid() on the child
275 		if (waitpid(pid, &status, 0) > 0) {
276 			if (WIFEXITED(status) && !WEXITSTATUS(status)) {
277 				// The program terminated normally and executed
278 				// successfully
279 				return EXIT_SUCCESS;
280 			} else if (WIFEXITED(status) && WEXITSTATUS(status)) {
281 				// Either "command not found" (WEXITSTATUS(status) == 127),
282 				// "permission denied" (not executable) (WEXITSTATUS(status) ==
283 				// 126) or the program terminated normally, but returned a
284 				// non-zero status. These exit codes will be handled by the
285 				// system shell itself, since we're using here execle()
286 				return WEXITSTATUS(status);
287 			} else {
288 				// The program didn't terminate normally
289 				return EXCRASHERR;
290 			}
291 		} else {
292 			// Waitpid() failed
293 			fprintf(stderr, "%s: waitpid: %s\n", PROGRAM_NAME,
294 			    strerror(errno));
295 			return errno;
296 		}
297 	}
298 
299 	// Never reached
300 	return EXIT_FAILURE; */
301 }
302 
303 /* Execute a command and return the corresponding exit status. The exit
304  * status could be: zero, if everything went fine, or a non-zero value
305  * in case of error. The function takes as first arguement an array of
306  * strings containing the command name to be executed and its arguments
307  * (cmd), an integer (bg) specifying if the command should be
308  * backgrounded (1) or not (0), and a flag to control file descriptors */
309 int
launch_execve(char ** cmd,int bg,int xflags)310 launch_execve(char **cmd, int bg, int xflags)
311 {
312 	if (!cmd)
313 		return EXNULLERR;
314 
315 	/* Reenable SIGCHLD, in case it was disabled. Otherwise, waitpid
316 	 * won't be able to catch error codes coming from the child. */
317 	signal(SIGCHLD, SIG_DFL);
318 
319 	pid_t pid = fork();
320 	if (pid < 0) {
321 		fprintf(stderr, "%s: fork: %s\n", PROGRAM_NAME, strerror(errno));
322 		return errno;
323 	} else if (pid == 0) {
324 		if (!bg) {
325 			/* If the program runs in the foreground, reenable signals
326 			 * only for the child, in case they were disabled for the
327 			 * parent */
328 			signal(SIGHUP, SIG_DFL);
329 			signal(SIGINT, SIG_DFL);
330 			signal(SIGQUIT, SIG_DFL);
331 			signal(SIGTERM, SIG_DFL);
332 		}
333 
334 		if (xflags) {
335 			int fd = open("/dev/null", O_WRONLY, 0200);
336 
337 			if (xflags & E_NOSTDIN)
338 				dup2(fd, STDIN_FILENO);
339 
340 			if (xflags & E_NOSTDOUT)
341 				dup2(fd, STDOUT_FILENO);
342 
343 			if (xflags & E_NOSTDERR)
344 				dup2(fd, STDERR_FILENO);
345 
346 			close(fd);
347 		}
348 
349 		execvp(cmd[0], cmd);
350 		fprintf(stderr, "%s: %s: %s\n", PROGRAM_NAME, cmd[0],
351 		    strerror(errno));
352 		_exit(errno);
353 	}
354 
355 	/* Get command status (pid > 0) */
356 	else {
357 		if (bg) {
358 			run_in_background(pid);
359 			return EXIT_SUCCESS;
360 		} else {
361 			return run_in_foreground(pid);
362 		}
363 	}
364 
365 	/* Never reached */
366 	return EXIT_FAILURE;
367 }
368 
369 static int
run_shell_cmd(char ** comm)370 run_shell_cmd(char **comm)
371 {
372 	/* LOG EXTERNAL COMMANDS
373 	* 'no_log' will be true when running profile or prompt commands */
374 	if (!no_log)
375 		log_function(comm);
376 
377 	/* PREVENT UNGRACEFUL EXIT */
378 	/* Prevent the user from killing the program via the 'kill',
379 	 * 'pkill' or 'killall' commands, from within CliFM itself.
380 	 * Otherwise, the program will be forcefully terminated without
381 	 * freeing allocated memory */
382 	if ((*comm[0] == 'k' || *comm[0] == 'p') && (strcmp(comm[0], "kill") == 0
383 	|| strcmp(comm[0], "killall") == 0 || strcmp(comm[0], "pkill") == 0)) {
384 		size_t i;
385 		for (i = 1; i <= args_n; i++) {
386 			if ((strcmp(comm[0], "kill") == 0 && atoi(comm[i]) == (int)own_pid)
387 			|| ((strcmp(comm[0], "killall") == 0 || strcmp(comm[0], "pkill") == 0)
388 			&& strcmp(comm[i], argv_bk[0]) == 0)) {
389 				fprintf(stderr, _("%s: To gracefully quit enter 'quit'\n"),
390 						PROGRAM_NAME);
391 				return EXIT_FAILURE;
392 			}
393 		}
394 	}
395 
396 	/* CHECK WHETHER SHELL COMMANDS ARE ALLOWED */
397 	if (!ext_cmd_ok) {
398 		fprintf(stderr, _("%s: External commands are not allowed. "
399 				  "Run 'ext on' to enable them.\n"), PROGRAM_NAME);
400 		return EXIT_FAILURE;
401 	}
402 
403 	if (*comm[0] == *argv_bk[0] && strcmp(comm[0], argv_bk[0]) == 0) {
404 		fprintf(stderr, "%s: Nested instances are not allowed\n",
405 		    PROGRAM_NAME);
406 		return EXIT_FAILURE;
407 	}
408 
409 	/*
410 	 * By making precede the command by a colon or a semicolon, the
411 	 * user can BYPASS CliFM parsing, expansions, and checks to be
412 	 * executed DIRECTLY by the system shell (execle). For example:
413 	 * if the amount of files listed on the screen (ELN's) is larger
414 	 * or equal than 644 and the user tries to issue this command:
415 	 * "chmod 644 filename", CLIFM will take 644 to be an ELN, and
416 	 * will thereby try to expand it into the corresponding file name,
417 	 * which is not what the user wants. To prevent this, simply run
418 	 * the command as follows: ";chmod 644 filename" */
419 	char *first = comm[0];
420 	if (*comm[0] == ':' || *comm[0] == ';')
421 		first++;
422 
423 	/* #### RUN THE SHELL COMMAND #### */
424 
425 	/* Store the command and each argument into a single array to be
426 	 * executed by execle() using the system shell (/bin/sh -c) */
427 	char *cmd = (char *)NULL;
428 	size_t len = strlen(first) + 2;
429 	cmd = (char *)xnmalloc(len + (bg_proc ? 2 : 0), sizeof(char));
430 	strncpy(cmd, first, len);
431 
432 	size_t i;
433 	for (i = 1; comm[i]; i++) {
434 		cmd[len - 2] = ' ';
435 		cmd[len - 1] = '\0';
436 		len += strlen(comm[i]) + 1;
437 		cmd = (char *)xrealloc(cmd, (len + 2 + (bg_proc ? 2 : 0))
438 				* sizeof(char));
439 		strncat(cmd, comm[i], len);
440 	}
441 
442 	/* Append final ampersand if backgrounded */
443 	if (bg_proc) {
444 		cmd[len - 2] = '&';
445 		cmd[len - 1] = '\0';
446 	}
447 /*
448 	// Since we modified LS_COLORS, store its current value and unset
449 	// it. Some shell commands use LS_COLORS to display their outputs
450 	// ("ls -l", for example, use the "no" value to print file
451 	// properties). So, we unset it to prevent wrong color output
452 	// for external commands. The disadvantage of this procedure is
453 	// that if the user uses a customized LS_COLORS, unsetting it
454 	// set its value to default, and the customization is lost.
455 
456 #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
457 	char *my_ls_colors = (char *)NULL, *p = (char *)NULL;
458 	// For some reason, when running on FreeBSD Valgrind complains
459 	// about overlapping source and destiny in setenv() if I just
460 	// copy the address returned by getenv() instead of the string
461 	// itself. Not sure why, but this makes the error go away
462 	p = getenv("LS_COLORS");
463 	my_ls_colors = (char *)xnmalloc(strlen(p) + 1, sizeof(char *));
464 	strcpy(my_ls_colors, p);
465 	p = (char *)NULL;
466 
467 #else
468 	static char *my_ls_colors = (char *)NULL;
469 	my_ls_colors = getenv("LS_COLORS");
470 #endif
471 
472 	if (ls_colors_bk && *ls_colors_bk != '\0')
473 		setenv("LS_COLORS", ls_colors_bk, 1);
474 	else
475 		unsetenv("LS_COLORS"); */
476 
477 /*	if (launch_execle(cmd) != EXIT_SUCCESS)
478 		exit_status = EXIT_FAILURE; */
479 	int exit_status = launch_execle(cmd);
480 	free(cmd);
481 
482 	/* Restore LS_COLORS value to use CliFM colors */
483 /*	setenv("LS_COLORS", my_ls_colors, 1);
484 
485 #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
486 	free(my_ls_colors);
487 #endif */
488 
489 	/* Reload the list of available commands in PATH for TAB completion.
490 	 * Why? If this list is not updated, whenever some new program is
491 	 * installed, renamed, or removed from some of the paths in PATH
492 	 * while in CliFM, this latter needs to be restarted in order
493 	 * to be able to recognize the new program for TAB completion */
494 	int j;
495 	if (bin_commands) {
496 		j = (int)path_progsn;
497 		while (--j >= 0)
498 			free(bin_commands[j]);
499 		free(bin_commands);
500 		bin_commands = (char **)NULL;
501 	}
502 
503 	if (paths) {
504 		j = (int)path_n;
505 		while (--j >= 0)
506 			free(paths[j]);
507 	}
508 
509 	path_n = (size_t)get_path_env();
510 	get_path_programs();
511 
512 	return exit_status;
513 }
514 
515 static int
set_max_files(char ** args)516 set_max_files(char **args)
517 {
518 	if (!args[1]) {
519 		if (max_files == -1)
520 			puts(_("Max files: unset"));
521 		else
522 			printf(_("Max files: %d\n"), max_files);
523 		return EXIT_SUCCESS;
524 	}
525 
526 	if (*args[1] == '-' && strcmp(args[1], "--help") == 0) {
527 		puts(_(MF_USAGE));
528 		return EXIT_SUCCESS;
529 	}
530 
531 	if (*args[1] == 'u' && strcmp(args[1], "unset") == 0) {
532 		max_files = -1;
533 		puts(_("Max files: unset"));
534 		return EXIT_SUCCESS;
535 	}
536 
537 	if (*args[1] == '0' && !args[1][1]) {
538 		max_files = 0;
539 		printf(_("Max files set to %d\n"), max_files);
540 		return EXIT_SUCCESS;
541 	}
542 
543 	long inum = strtol(args[1], NULL, 10);
544 	if (inum == LONG_MAX || inum == LONG_MIN || inum <= 0) {
545 		fprintf(stderr, _("%s: %s: Invalid number\n"), PROGRAM_NAME, args[1]);
546 		return (exit_code = EXIT_FAILURE);
547 	}
548 
549 	max_files = (int)inum;
550 	printf(_("Max files set to %d\n"), max_files);
551 	return EXIT_SUCCESS;
552 }
553 
554 
555 /* Take the command entered by the user, already splitted into substrings
556  * by parse_input_str(), and call the corresponding function. Return zero
557  * in case of success and one in case of error */
558 int
exec_cmd(char ** comm)559 exec_cmd(char **comm)
560 {
561 	fputs(df_c, stdout);
562 
563 	/* Exit flag. exit_code is zero (sucess) by default. In case of error
564 	 * in any of the functions below, it will be set to one (failure).
565 	 * This will be the value returned by this function. Used by the \z
566 	 * escape code in the prompt to print the exit status of the last
567 	 * executed command */
568 	int old_exit_code = exit_code;
569 	exit_code = EXIT_SUCCESS;
570 
571 	if (*comm[0] == '#')
572 		return exit_code;
573 
574 				/* ##########################
575 				 * #     	AUTOJUMP	    #
576 				 * ########################## */
577 
578 /*	if (autojump) {
579 		exit_code = run_autojump(comm);
580 		if (exit_code != -1)
581 			return exit_code;
582 	} */
583 
584 	/* Warn when using the ',' keyword and there's no pinned file */
585 	int k = (int)args_n + 1;
586 	while (--k >= 0) {
587 		if (*comm[k] == ',' && !comm[k][1]) {
588 			fprintf(stderr, _("%s: No pinned file\n"), PROGRAM_NAME);
589 			return (exit_code = EXIT_FAILURE);
590 		}
591 	}
592 
593 	/* User defined actions */
594 	if (actions_n) {
595 		int i = (int)actions_n;
596 		while (--i >= 0) {
597 			if (*comm[0] == *usr_actions[i].name
598 			&& strcmp(comm[0], usr_actions[i].name) == 0)
599 				return (exit_code = run_action(usr_actions[i].value, comm));
600 		}
601 	}
602 
603 	/* User defined variables */
604 	if (flags & IS_USRVAR_DEF) {
605 		flags &= ~IS_USRVAR_DEF;
606 		return (exit_code = create_usr_var(comm[0]));
607 	}
608 
609 	if (comm[0][0] == ';' || comm[0][0] == ':') {
610 		if (!comm[0][1]) {
611 			/* If just ":" or ";", launch the default shell */
612 			char *cmd[] = {user.shell, NULL};
613 			if (launch_execve(cmd, FOREGROUND, E_NOFLAG) != EXIT_SUCCESS)
614 				exit_code = EXIT_FAILURE;
615 			return exit_code;
616 		} else if (comm[0][1] == ';' || comm[0][1] == ':') {
617 		/* If double semi colon or colon (or ";:" or ":;") */
618 			fprintf(stderr, _("%s: '%s': Syntax error\n"), PROGRAM_NAME, comm[0]);
619 			return (exit_code = EXIT_FAILURE);
620 		}
621 	}
622 
623 				/* ###############################
624 				 * #    AUTOCD & AUTO-OPEN (1)   #
625 				 * ############################### */
626 
627 	char *deq_str = (char *)NULL;
628 	if (autocd || auto_open) {
629 		/* Expand tilde */
630 		if (*comm[0] == '~') {
631 			char *exp_path = tilde_expand(comm[0]);
632 			if (exp_path) {
633 				comm[0] = (char *)xrealloc(comm[0], (strlen(exp_path) + 1) * sizeof(char));
634 				strcpy(comm[0], exp_path);
635 				free(exp_path);
636 			}
637 		}
638 
639 		/* Deescape the string (only if file name) */
640 		if (strchr(comm[0], '\\')) {
641 			deq_str = dequote_str(comm[0], 0);
642 /*			if (deq_str) {
643 				if (access(deq_str, F_OK) == 0)
644 					strcpy(comm[0], deq_str);
645 				free(deq_str);
646 			} */
647 		}
648 	}
649 
650 	/* Only autocd or auto-open here if not absolute path and if there
651 	 * is no second argument or if second argument is "&" */
652 	if (*comm[0] != '/' && (autocd || auto_open) && (!comm[1]
653 	|| (*comm[1] == '&' && !comm[1][1]))) {
654 		char *tmp = deq_str ? deq_str : comm[0];
655 		size_t tmp_len = strlen(tmp);
656 		if (tmp[tmp_len - 1] == '/')
657 			tmp[tmp_len - 1] = '\0';
658 
659 		if (autocd && cdpath_n && !comm[1]
660 		&& cd_function(comm[0], CD_NO_PRINT_ERROR) == EXIT_SUCCESS) {
661 			free(deq_str);
662 			return EXIT_SUCCESS;
663 		}
664 
665 		int i = (int)files;
666 		while (--i >= 0) {
667 			if (*tmp != *file_info[i].name)
668 				continue;
669 
670 			if (strcmp(tmp, file_info[i].name) != 0)
671 				continue;
672 
673 			free(deq_str);
674 			deq_str = (char *)NULL;
675 
676 			if (autocd && (file_info[i].type == DT_DIR || file_info[i].dir == 1))
677 				return (exit_code = cd_function(comm[0], CD_PRINT_ERROR));
678 
679 			if (auto_open && (file_info[i].type == DT_REG
680 			|| file_info[i].type == DT_LNK)) {
681 				char *cmd[] = {"open", comm[0],
682 				    comm[1] ? comm[1] : NULL, NULL};
683 				return (exit_code = open_function(cmd));
684 			} else {
685 				break;
686 			}
687 		}
688 	}
689 
690 	free(deq_str);
691 
692 	/* The more often a function is used, the more on top should it be
693 	 * in this if...else..if chain. It will be found faster this way. */
694 
695 	/* ####################################################
696 	 * #                 BUILTIN COMMANDS                 #
697 	 * ####################################################*/
698 
699 	/*          ############### CD ##################     */
700 	if (*comm[0] == 'c' && comm[0][1] == 'd' && !comm[0][2]) {
701 		if (!comm[1])
702 			exit_code = cd_function(NULL, CD_PRINT_ERROR);
703 		else if (*comm[1] == '-' && strcmp(comm[1], "--help") == 0)
704 			puts(_(CD_USAGE));
705 		else
706 			exit_code = cd_function(comm[1], CD_PRINT_ERROR);
707 		return exit_code;
708 	}
709 
710 	/*         ############### OPEN ##################     */
711 	else if (*comm[0] == 'o' && (!comm[0][1] || strcmp(comm[0], "open") == 0)) {
712 		if (!comm[1]) {
713 			puts(_(OPEN_USAGE));
714 			exit_code = EXIT_FAILURE;
715 		} else if (*comm[1] == '-' && strcmp(comm[1], "--help") == 0) {
716 			puts(_(OPEN_USAGE));
717 		} else {
718 			exit_code = open_function(comm);
719 		}
720 		return exit_code;
721 	}
722 
723 	/*      ############### OPEN WITH ##################     */
724 	else if (*comm[0] == 'o' && comm[0][1] == 'w' && !comm[0][2]) {
725 #ifndef _NO_LIRA
726 		if (comm[1]) {
727 			if (*comm[1] == '-' && strcmp(comm[1], "--help") == 0) {
728 				puts(_(OW_USAGE));
729 				return EXIT_SUCCESS;
730 			}
731 			return mime_open_with(comm[1], comm[2] ? comm + 2 : NULL);
732 		}
733 		puts(_(OW_USAGE));
734 		return EXIT_SUCCESS;
735 #else
736 		fprintf(stderr, "%s: %s\n", PROGRAM_NAME, _(NOT_AVAILABLE));
737 		return EXIT_FAILURE;
738 #endif
739 	}
740 
741 	/*   ############## DIRECTORY JUMPER ##################     */
742 	else if (*comm[0] == 'j' && (!comm[0][1] || ((comm[0][1] == 'c'
743 	|| comm[0][1] == 'p' || comm[0][1] == 'e' || comm[0][1] == 'o'
744 	|| comm[0][1] == 'l') && !comm[0][2])))
745 		return (exit_code = dirjump(comm, NO_SUG_JUMP));
746 
747 	/*       ############### REFRESH ##################     */
748 	else if (*comm[0] == 'r' && ((comm[0][1] == 'f' && !comm[0][2])
749 	|| strcmp(comm[0], "refresh") == 0)) {
750 		if (autols) {
751 			free_dirlist();
752 			list_dir();
753 		}
754 		return exit_code = old_exit_code;
755 	}
756 
757 	/*     ############### BOOKMARKS ##################     */
758 	else if (*comm[0] == 'b' && ((comm[0][1] == 'm' && !comm[0][2])
759 	|| strcmp(comm[0], "bookmarks") == 0)) {
760 		if (comm[1] && strcmp(comm[1], "--help") == 0) {
761 			puts(_(BOOKMARKS_USAGE));
762 			return EXIT_SUCCESS;
763 		}
764 		/* Disable keyboard shortcuts. Otherwise, the function will
765 		 * still be waiting for input while the screen have been taken
766 		 * by another function */
767 		kbind_busy = 1;
768 		/* Disable TAB completion while in Bookmarks */
769 		rl_attempted_completion_function = NULL;
770 		exit_code = bookmarks_function(comm);
771 		/* Reenable TAB completion */
772 		rl_attempted_completion_function = my_rl_completion;
773 		/* Reenable keyboard shortcuts */
774 		kbind_busy = 0;
775 		return exit_code;
776 	}
777 
778 	/*       ############### BACK AND FORTH ##################     */
779 	else if (*comm[0] == 'b' && (!comm[0][1] || strcmp(comm[0], "back") == 0))
780 		return (exit_code = back_function(comm));
781 
782 	else if (*comm[0] == 'f' && (!comm[0][1] || strcmp(comm[0], "forth") == 0))
783 		return (exit_code = forth_function(comm));
784 
785 	else if ((*comm[0] == 'b' && comm[0][1] == 'h' && !comm[0][2])
786 	|| (*comm[0] == 'f' && comm[0][1] == 'h' && !comm[0][2])) {
787 		print_dirhist();
788 		return EXIT_SUCCESS;
789 	}
790 
791 
792 	/*     ################# NEW FILE ##################     */
793 	else if (*comm[0] == 'n' && (!comm[0][1] || strcmp(comm[0], "new") == 0))
794 		exit_code = create_file(comm);
795 
796 	/*     ############### DUPLICATE FILE ##################     */
797 	else if (*comm[0] == 'd' && (!comm[0][1] || strcmp(comm[0], "dup") == 0)) {
798 		if (!comm[1] || (*comm[1] == '-' && strcmp(comm[1], "--help") == 0)) {
799 			puts(DUP_USAGE);
800 			return EXIT_SUCCESS;
801 		}
802 		exit_code = dup_file(comm[1], comm[2] ? comm[2] : NULL);
803 	}
804 
805 #ifdef __HAIKU__
806 	else if ((*comm[0] == 'c' || *comm[0] == 'r' || *comm[0] == 'm'
807 	|| *comm[0] == 't' || *comm[0] == 'u' || *comm[0] == 'l')
808 	&& (strcmp(comm[0], "cp") == 0 || strcmp(comm[0], "rm") == 0
809 	|| strcmp(comm[0], "mkdir") == 0 || strcmp(comm[0], "unlink") == 0
810 	|| strcmp(comm[0], "touch") == 0 || strcmp(comm[0], "ln") == 0
811 	|| strcmp(comm[0], "chmod") == 0))
812 		return (exit_code = run_and_refresh(comm));
813 #endif
814 
815 	/*     ############### COPY AND MOVE ##################     */
816 	else if ((*comm[0] == 'c' && (!comm[0][1] || (comm[0][1] == 'p'
817 	&& !comm[0][2])))
818 
819 		|| (*comm[0] == 'm' && (!comm[0][1] || (comm[0][1] == 'v'
820 		&& !comm[0][2])))
821 
822 		|| (*comm[0] == 'v' && (!comm[0][1] || (comm[0][1] == 'v'
823 		&& !comm[0][2])))
824 
825 		|| (*comm[0] == 'p' && strcmp(comm[0], "paste") == 0)) {
826 
827 		if (((*comm[0] == 'c' || *comm[0] == 'v') && !comm[0][1])
828 		|| (*comm[0] == 'v' && comm[0][1] == 'v' && !comm[0][2])
829 		|| strcmp(comm[0], "paste") == 0) {
830 
831 			if (comm[1] && *comm[1] == '-' && strcmp(comm[1], "--help") == 0) {
832 				puts(_(WRAPPERS_USAGE));
833 				return EXIT_SUCCESS;
834 			}
835 
836 			if (*comm[0] == 'v' && comm[0][1] == 'v' && !comm[0][2])
837 				copy_n_rename = 1;
838 
839 			comm[0] = (char *)xrealloc(comm[0], 12 * sizeof(char));
840 			if (!copy_n_rename) {
841 				if (cp_cmd == CP_CP)
842 					strcpy(comm[0], "cp -iRp");
843 				else if (cp_cmd == CP_ADVCP)
844 					strcpy(comm[0], "advcp -giRp");
845 				else
846 					strcpy(comm[0], "wcp");
847 			} else {
848 				strcpy(comm[0], "cp");
849 			}
850 		} else if (*comm[0] == 'm' && !comm[0][1]) {
851 			if (comm[1] && *comm[1] == '-' && strcmp(comm[1], "--help") == 0) {
852 				puts(_(WRAPPERS_USAGE));
853 				return EXIT_SUCCESS;
854 			}
855 			if (!sel_is_last && comm[1] && !comm[2])
856 				xrename = 1;
857 
858 			comm[0] = (char *)xrealloc(comm[0], 10 * sizeof(char));
859 			if (mv_cmd == MV_MV)
860 				strcpy(comm[0], "mv -i");
861 			else
862 				strcpy(comm[0], "advmv -gi");
863 		}
864 
865 		kbind_busy = 1;
866 		exit_code = copy_function(comm);
867 		kbind_busy = 0;
868 	}
869 
870 	/*         ############### TRASH ##################     */
871 	else if (*comm[0] == 't' && (!comm[0][1] || strcmp(comm[0], "tr") == 0
872 	|| strcmp(comm[0], "trash") == 0)) {
873 #ifndef _NO_TRASH
874 		if (comm[1] && *comm[1] == '-' && strcmp(comm[1], "--help") == 0) {
875 			puts(_(TRASH_USAGE));
876 			return EXIT_SUCCESS;
877 		}
878 
879 		exit_code = trash_function(comm);
880 
881 		if (is_sel) { /* If 'tr sel', deselect everything */
882 			int i = (int)sel_n;
883 			while (--i >= 0)
884 				free(sel_elements[i]);
885 			sel_n = 0;
886 			if (save_sel() != 0)
887 				exit_code = EXIT_FAILURE;
888 		}
889 #else
890 		fprintf(stderr, _("%s: trash: %s\n"), PROGRAM_NAME, _(NOT_AVAILABLE));
891 		return EXIT_FAILURE;
892 #endif /* !_NO_TRASH */
893 	}
894 
895 	else if (*comm[0] == 'u' && (!comm[0][1] || strcmp(comm[0], "undel") == 0
896 	|| strcmp(comm[0], "untrash") == 0)) {
897 #ifndef _NO_TRASH
898 		if (comm[1] && *comm[1] == '-' && strcmp(comm[1], "--help") == 0) {
899 			puts(_(UNTRASH_USAGE));
900 			return EXIT_SUCCESS;
901 		}
902 
903 		kbind_busy = 1;
904 		rl_attempted_completion_function = NULL;
905 		exit_code = untrash_function(comm);
906 		rl_attempted_completion_function = my_rl_completion;
907 		kbind_busy = 0;
908 #else
909 		fprintf(stderr, _("%s: trash: %s\n"), PROGRAM_NAME, _(NOT_AVAILABLE));
910 		return EXIT_FAILURE;
911 #endif /* !_NO_TRASH */
912 	}
913 
914 	/*     ############### SELECTION ##################     */
915 	else if (*comm[0] == 's' && (!comm[0][1] || strcmp(comm[0], "sel") == 0))
916 		return (exit_code = sel_function(comm));
917 
918 	else if (*comm[0] == 's' && (strcmp(comm[0], "sb") == 0
919 	|| strcmp(comm[0], "selbox") == 0)) {
920 		show_sel_files();
921 		return EXIT_SUCCESS;
922 	}
923 
924 	else if (*comm[0] == 'd' && (strcmp(comm[0], "ds") == 0
925 	|| strcmp(comm[0], "desel") == 0)) {
926 		if (comm[1] && *comm[1] == '-' && strcmp(comm[1], "--help") == 0) {
927 			puts(_(DESEL_USAGE));
928 			return EXIT_SUCCESS;
929 		}
930 
931 		kbind_busy = 1;
932 		rl_attempted_completion_function = NULL;
933 		exit_code = deselect(comm);
934 		rl_attempted_completion_function = my_rl_completion;
935 		kbind_busy = 0;
936 		return exit_code;
937 	}
938 
939 	/*  ############# SOME SHELL CMD WRAPPERS ###############  */
940 
941 	else if ((*comm[0] == 'r' || *comm[0] == 'm' || *comm[0] == 'l')
942 	&& (strcmp(comm[0], "r") == 0 || strcmp(comm[0], "l") == 0
943 	|| strcmp(comm[0], "md") == 0 || strcmp(comm[0], "le") == 0)) {
944 		/* This help is only for c, l, le, m, r, t, and md commands */
945 		if (comm[1] && *comm[1] == '-' && strcmp(comm[1], "--help") == 0) {
946 			puts(_(WRAPPERS_USAGE));
947 			return EXIT_SUCCESS;
948 		}
949 
950 		if (*comm[0] == 'l' && !comm[0][1]) {
951 			comm[0] = (char *)xrealloc(comm[0], 7 * sizeof(char));
952 			strcpy(comm[0], "ln -sn");
953 
954 			/* Make sure the symlink source is always an absolute path */
955 			if (comm[1] && *comm[1] != '/' && *comm[1] != '~') {
956 				size_t len = strlen(comm[1]);
957 				char *tmp = (char *)xnmalloc(len + 1, sizeof(char));
958 				xstrsncpy(tmp, comm[1], len);
959 				comm[1] = (char *)xrealloc(comm[1], (len
960 							+ strlen(ws[cur_ws].path) + 2) * sizeof(char));
961 				sprintf(comm[1], "%s/%s", ws[cur_ws].path, tmp);
962 				free(tmp);
963 			}
964 		} else if (*comm[0] == 'r' && !comm[0][1]) {
965 			exit_code = remove_file(comm);
966 			goto CHECK_EVENTS;
967 		} else if (*comm[0] == 'm' && comm[0][1] == 'd' && !comm[0][2]) {
968 			comm[0] = (char *)xrealloc(comm[0], 9 * sizeof(char));
969 			strcpy(comm[0], "mkdir -p");
970 		}
971 
972 		if (*comm[0] == 'l' && comm[0][1] == 'e' && !comm[0][2]) {
973 			if (!comm[1]) {
974 				fprintf(stderr, "%s\n", _(LE_USAGE));
975 				return (exit_code = EXIT_FAILURE);
976 			}
977 			exit_code = edit_link(comm[1]);
978 			goto CHECK_EVENTS;
979 		} else if (*comm[0] == 'l' && comm[0][1] == 'n' && !comm[0][2]) {
980 			if (comm[1] && (strcmp(comm[1], "edit") == 0
981 			|| strcmp(comm[1], "e") == 0)) {
982 				if (!comm[2]) {
983 					fprintf(stderr, "%s\n", _(LE_USAGE));
984 					return (exit_code = EXIT_FAILURE);
985 				}
986 				exit_code = edit_link(comm[2]);
987 				goto CHECK_EVENTS;
988 			}
989 		}
990 
991 		kbind_busy = 1;
992 		exit_code = run_and_refresh(comm);
993 		kbind_busy = 0;
994 	}
995 
996 	/*    ############### TOGGLE EXEC ##################     */
997 	else if (*comm[0] == 't' && comm[0][1] == 'e' && !comm[0][2]) {
998 		if (!comm[1] || (*comm[1] == '-' && strcmp(comm[1], "--help") == 0)) {
999 			puts(_(TE_USAGE));
1000 			return EXIT_SUCCESS;
1001 		}
1002 
1003 		size_t j;
1004 		for (j = 1; comm[j]; j++) {
1005 			struct stat attr;
1006 			if (strchr(comm[j], '\\')) {
1007 				char *tmp = dequote_str(comm[j], 0);
1008 				if (tmp) {
1009 					strcpy(comm[j], tmp);
1010 					free(tmp);
1011 				}
1012 			}
1013 
1014 			if (lstat(comm[j], &attr) == -1) {
1015 				fprintf(stderr, "stat: %s: %s\n", comm[j], strerror(errno));
1016 				exit_code = EXIT_FAILURE;
1017 				continue;
1018 			}
1019 
1020 			if (xchmod(comm[j], attr.st_mode) == -1)
1021 				exit_code = EXIT_FAILURE;
1022 		}
1023 
1024 		if (exit_code == EXIT_SUCCESS)
1025 			printf(_("%s: Toggled executable bit on %zu file(s)\n"),
1026 			    PROGRAM_NAME, args_n);
1027 	}
1028 
1029 	/*    ############### PINNED FILE ##################     */
1030 	else if (*comm[0] == 'p' && strcmp(comm[0], "pin") == 0) {
1031 		if (comm[1]) {
1032 			if (*comm[1] == '-' && strcmp(comm[1], "--help") == 0)
1033 				puts(PIN_USAGE);
1034 			else
1035 				exit_code = pin_directory(comm[1]);
1036 		} else {
1037 			if (pinned_dir)
1038 				printf(_("pinned file: %s\n"), pinned_dir);
1039 			else
1040 				puts(_("No pinned file"));
1041 		}
1042 		return exit_code;
1043 	}
1044 
1045 	else if (*comm[0] == 'u' && strcmp(comm[0], "unpin") == 0)
1046 		return (exit_code = unpin_dir());
1047 
1048 	/*    ############### PROPERTIES ##################     */
1049 	else if (*comm[0] == 'p' && (!comm[0][1] || strcmp(comm[0], "pr") == 0
1050 	|| strcmp(comm[0], "pp") == 0 || strcmp(comm[0], "prop") == 0)) {
1051 		if (!comm[1]) {
1052 			fprintf(stderr, "%s\n", _(PROP_USAGE));
1053 			exit_code = EXIT_FAILURE;
1054 			return EXIT_FAILURE;
1055 		} else if (*comm[1] == '-' && strcmp(comm[1], "--help") == 0) {
1056 			puts(_(PROP_USAGE));
1057 			return EXIT_SUCCESS;
1058 		}
1059 
1060 		return (exit_code = properties_function(comm));
1061 	}
1062 
1063 	/*     ############### SEARCH ##################     */
1064 	else if (*comm[0] == '/' && !strchr(comm[0], '\\')
1065 	&& access(comm[0], F_OK) != 0) {
1066 		/* If not absolute path */
1067 		/* Try first globbing, and if no result, try regex */
1068 		if (search_glob(comm, (comm[0][1] == '!') ? 1 : 0) == EXIT_FAILURE)
1069 			exit_code = search_regex(comm, (comm[0][1] == '!') ? 1 : 0,
1070 					case_sens_search ? 1 : 0);
1071 		else
1072 			exit_code = EXIT_SUCCESS;
1073 		return exit_code;
1074 	}
1075 
1076 	/*      ############## HISTORY ##################     */
1077 	else if (*comm[0] == '!' && comm[0][1] != ' ' && comm[0][1] != '\t'
1078 	&& comm[0][1] != '\n' && comm[0][1] != '=' && comm[0][1] != '(')
1079 		exit_code = run_history_cmd(comm[0] + 1);
1080 
1081 	/*    ############### BATCH LINK ##################     */
1082 	else if (*comm[0] == 'b' && comm[0][1] == 'l' && !comm[0][2])
1083 		exit_code = batch_link(comm);
1084 
1085 	/*    ############### BULK RENAME ##################     */
1086 	else if (*comm[0] == 'b' && ((comm[0][1] == 'r' && !comm[0][2])
1087 	|| strcmp(comm[0], "bulk") == 0)) {
1088 		if (!comm[1]) {
1089 			fprintf(stderr, "%s\n", _(BULK_USAGE));
1090 			return (exit_code = EXIT_FAILURE);
1091 		}
1092 
1093 		if (*comm[1] == '-' && strcmp(comm[1], "--help") == 0) {
1094 			puts(_(BULK_USAGE));
1095 			return EXIT_SUCCESS;
1096 		}
1097 		exit_code = bulk_rename(comm);
1098 	}
1099 
1100 	/*      ################ SORT ##################     */
1101 	else if (*comm[0] == 's' && ((comm[0][1] == 't' && !comm[0][2])
1102 	|| strcmp(comm[0], "sort") == 0)) {
1103 		if (comm[1] && *comm[1] == '-' && strcmp(comm[1], "--help") == 0) {
1104 			puts(_(SORT_USAGE));
1105 			return EXIT_SUCCESS;
1106 		}
1107 		return (exit_code = sort_function(comm));
1108 	}
1109 
1110 	/*    ########## FILE NAMES CLEANER ############## */
1111 	else if (*comm[0] == 'b' && ((comm[0][1] == 'b' && !comm[0][2])
1112 	|| strcmp(comm[0], "bleach") == 0)) {
1113 		if (comm[1] && *comm[1] == '-' && strcmp(comm[1], "--help") == 0) {
1114 			puts(_(BLEACH_USAGE));
1115 			return EXIT_SUCCESS;
1116 		}
1117 #ifndef _NO_BLEACH
1118 		exit_code = bleach_files(comm);
1119 #else
1120 		fprintf(stderr, _("%s: bleach: %s\n"), PROGRAM_NAME, NOT_AVAILABLE);
1121 		return EXIT_FAILURE;
1122 #endif
1123 	}
1124 
1125 	/*   ################ ARCHIVER ##################     */
1126 	else if (*comm[0] == 'a' && ((comm[0][1] == 'c' || comm[0][1] == 'd')
1127 	&& !comm[0][2])) {
1128 #ifndef _NO_ARCHIVING
1129 		if (!comm[1] || (*comm[1] == '-' && strcmp(comm[1], "--help") == 0)) {
1130 			puts(_(ARCHIVE_USAGE));
1131 			return EXIT_SUCCESS;
1132 		}
1133 
1134 		if (comm[0][1] == 'c')
1135 			exit_code = archiver(comm, 'c');
1136 		else
1137 			exit_code = archiver(comm, 'd');
1138 #else
1139 		fprintf(stderr, _("%s: archiving: %s\n"), PROGRAM_NAME, _(NOT_AVAILABLE));
1140 		return EXIT_FAILURE;
1141 #endif
1142 	}
1143 
1144 	/* ##################################################
1145 	 * #                 MINOR FUNCTIONS                #
1146 	 * ##################################################*/
1147 
1148 	else if (*comm[0] == 'w' && comm[0][1] == 's' && !comm[0][2])
1149 		return (exit_code = workspaces(comm[1] ? comm[1] : NULL));
1150 
1151 	else if (*comm[0] == 'f' && ((comm[0][1] == 't' && !comm[0][2])
1152 	|| strcmp(comm[0], "filter") == 0))
1153 		return (exit_code = filter_function(comm[1]));
1154 
1155 	else if (*comm[0] == 'c' && ((comm[0][1] == 'l' && !comm[0][2])
1156 	|| strcmp(comm[0], "columns") == 0)) {
1157 		if (!comm[1] || (*comm[1] == '-' && strcmp(comm[1], "--help") == 0)) {
1158 			puts(_(COLUMNS_USAGE));
1159 			return EXIT_SUCCESS;
1160 		} else if (*comm[1] == 'o' && comm[1][1] == 'n' && !comm[1][2]) {
1161 			columned = 1;
1162 			if (autols) {
1163 				free_dirlist();
1164 				exit_code = list_dir();
1165 			}
1166 		} else if (*comm[1] == 'o' && strcmp(comm[1], "off") == 0) {
1167 			columned = 0;
1168 			if (autols) {
1169 				free_dirlist();
1170 				exit_code = list_dir();
1171 			}
1172 		} else {
1173 			fprintf(stderr, "%s\n", _(COLUMNS_USAGE));
1174 			exit_code = EXIT_FAILURE;
1175 			return EXIT_FAILURE;
1176 		}
1177 		return exit_code;
1178 	}
1179 	else if (*comm[0] == 'i' && strcmp(comm[0], "icons") == 0) {
1180 #ifndef _NO_ICONS
1181 		if (!comm[1] || (*comm[1] == '-' && strcmp(comm[1], "--help") == 0)) {
1182 			puts(_(ICONS_USAGE));
1183 		} else if (*comm[1] == 'o' && comm[1][1] == 'n' && !comm[1][2]) {
1184 			icons = 1;
1185 			if (autols) {
1186 				free_dirlist();
1187 				exit_code = list_dir();
1188 			}
1189 		} else if (*comm[1] == 'o' && strcmp(comm[1], "off") == 0) {
1190 			icons = 0;
1191 			if (autols) {
1192 				free_dirlist();
1193 				exit_code = list_dir();
1194 			}
1195 		} else {
1196 			fprintf(stderr, "%s\n", _(ICONS_USAGE));
1197 			exit_code = EXIT_FAILURE;
1198 			return EXIT_FAILURE;
1199 		}
1200 
1201 		return EXIT_SUCCESS;
1202 #else
1203 		fprintf(stderr, _("%s: icons: %s\n"), PROGRAM_NAME, _(NOT_AVAILABLE));
1204 		return EXIT_SUCCESS;
1205 #endif /* _NO_ICONS */
1206 	}
1207 
1208 	else if (*comm[0] == 'c' && ((comm[0][1] == 's' && !comm[0][2])
1209 	|| strcmp(comm[0], "colorschemes") == 0))
1210 		return (exit_code = cschemes_function(comm));
1211 
1212 	else if (*comm[0] == 'k' && ((comm[0][1] == 'b' && !comm[0][2])
1213 	|| strcmp(comm[0], "keybinds") == 0))
1214 		return (exit_code = kbinds_function(comm));
1215 
1216 	else if (*comm[0] == 'e' && strcmp(comm[0], "exp") == 0) {
1217 		if (comm[1] && *comm[1] == '-' && strcmp(comm[1], "--help") == 0) {
1218 			puts(_(EXPORT_USAGE));
1219 			return EXIT_SUCCESS;
1220 		}
1221 
1222 		char *ret = export(comm, 1);
1223 		if (ret) {
1224 			printf("Files exported to: %s\n", ret);
1225 			free(ret);
1226 			return EXIT_SUCCESS;
1227 		}
1228 
1229 		return (exit_code = EXIT_FAILURE);
1230 	}
1231 
1232 	else if (*comm[0] == 'o' && strcmp(comm[0], "opener") == 0) {
1233 		if (!comm[1]) {
1234 			printf("opener: %s\n", (opener) ? opener : "lira (built-in)");
1235 			return EXIT_SUCCESS;
1236 		}
1237 		if (*comm[1] == '-' && strcmp(comm[1], "--help") == 0) {
1238 			puts(_(OPENER_USAGE));
1239 			return EXIT_SUCCESS;
1240 		}
1241 		if (opener) {
1242 			free(opener);
1243 			opener = (char *)NULL;
1244 		}
1245 		if (strcmp(comm[1], "default") != 0 && strcmp(comm[1], "lira") != 0) {
1246 			opener = (char *)xnmalloc(strlen(comm[1]) + 1, sizeof(char));
1247 			strcpy(opener, comm[1]);
1248 		}
1249 		printf(_("opener: Opener set to '%s'\n"), (opener) ? opener
1250 								   : "lira (built-in)");
1251 		return EXIT_SUCCESS;
1252 	}
1253 
1254 	/* #### TIPS #### */
1255 	else if (*comm[0] == 't' && strcmp(comm[0], "tips") == 0) {
1256 		print_tips(1);
1257 		return EXIT_SUCCESS;
1258 	}
1259 
1260 	/* #### ACTIONS #### */
1261 	else if (*comm[0] == 'a' && strcmp(comm[0], "actions") == 0) {
1262 		if (!comm[1]) {
1263 			if (actions_n) {
1264 				size_t i;
1265 				for (i = 0; i < actions_n; i++)
1266 					printf("%s %s->%s %s\n", usr_actions[i].name,
1267 					    mi_c, df_c, usr_actions[i].value);
1268 			} else {
1269 				puts(_("actions: No actions defined. Use the 'actions "
1270 				       "edit' command to add some"));
1271 			}
1272 		} else if (strcmp(comm[1], "edit") == 0) {
1273 			return (exit_code = edit_actions());
1274 		} else if (*comm[1] == '-' && strcmp(comm[1], "--help") == 0) {
1275 			puts(_(ACTIONS_USAGE));
1276 		} else {
1277 			fprintf(stderr, "%s\n", _(ACTIONS_USAGE));
1278 			exit_code = EXIT_FAILURE;
1279 			return EXIT_FAILURE;
1280 		}
1281 		return exit_code;
1282 	}
1283 
1284 	/* #### LIGHT MODE #### */
1285 	else if (*comm[0] == 'l' && comm[0][1] == 'm' && !comm[0][2]) {
1286 		if (comm[1]) {
1287 			if (*comm[1] == 'o' && strcmp(comm[1], "on") == 0) {
1288 				light_mode = 1;
1289 				puts(_("Light mode is on"));
1290 			} else if (*comm[1] == 'o' && strcmp(comm[1], "off") == 0) {
1291 				light_mode = 0;
1292 				puts(_("Light mode is off"));
1293 			} else {
1294 				puts(_(LM_USAGE));
1295 				exit_code = EXIT_FAILURE;
1296 			}
1297 		} else {
1298 			fprintf(stderr, "%s\n", _(LM_USAGE));
1299 			exit_code = EXIT_FAILURE;
1300 		}
1301 		return exit_code;
1302 	}
1303 
1304 	/*    ############### RELOAD ##################     */
1305 	else if (*comm[0] == 'r' && ((comm[0][1] == 'l' && !comm[0][2])
1306 	|| strcmp(comm[0], "reload") == 0)) {
1307 		exit_code = reload_config();
1308 		welcome_message = 0;
1309 		if (autols) {
1310 			free_dirlist();
1311 			if (list_dir() != EXIT_SUCCESS)
1312 				exit_code = EXIT_FAILURE;
1313 		}
1314 		return exit_code;
1315 	}
1316 
1317 	/* #### NEW INSTANCE #### */
1318 	else if ((*comm[0] == 'x' || *comm[0] == 'X') && !comm[0][1]) {
1319 		if (comm[1]) {
1320 			if (*comm[1] == '-' && strcmp(comm[1], "--help") == 0) {
1321 				puts(_(X_USAGE));
1322 				return EXIT_SUCCESS;
1323 			} else if (*comm[0] == 'x') {
1324 				exit_code = new_instance(comm[1], 0);
1325 			} else { /* Run as root */
1326 				exit_code = new_instance(comm[1], 1);
1327 			}
1328 		} else {
1329 		/* Run new instance in CWD */
1330 			if (*comm[0] == 'x')
1331 				exit_code = new_instance(ws[cur_ws].path, 0);
1332 			else
1333 				exit_code = new_instance(ws[cur_ws].path, 1);
1334 		}
1335 
1336 		return exit_code;
1337 	}
1338 
1339 	/* #### NET #### */
1340 	else if (*comm[0] == 'n' && (strcmp(comm[0], "net") == 0))
1341 		return (exit_code = remotes_function(comm));
1342 
1343 	/* #### MIME #### */
1344 	else if (*comm[0] == 'm' && ((comm[0][1] == 'm' && !comm[0][2])
1345 	|| strcmp(comm[0], "mime") == 0)) {
1346 #ifndef _NO_LIRA
1347 		return (exit_code = mime_open(comm));
1348 #else
1349 		fprintf(stderr, _("%s: Lira: %s\n"), PROGRAM_NAME, _(NOT_AVAILABLE));
1350 		return EXIT_FAILURE;
1351 #endif
1352 	}
1353 
1354 	else if (*comm[0] == 'l' && comm[0][1] == 's' && !comm[0][2] && !autols) {
1355 		free_dirlist();
1356 		exit_code = list_dir();
1357 		if (get_sel_files() != EXIT_SUCCESS)
1358 			exit_code = EXIT_FAILURE;
1359 		return exit_code;
1360 	}
1361 
1362 	/* #### PROFILE #### */
1363 	else if (*comm[0] == 'p' && ((comm[0][1] == 'f' && !comm[0][2]) || strcmp(comm[0], "prof") == 0 || strcmp(comm[0], "profile") == 0))
1364 		return (exit_code = profile_function(comm));
1365 
1366 	/* #### MOUNTPOINTS #### */
1367 	else if (*comm[0] == 'm' && ((comm[0][1] == 'p' && !comm[0][2]) || strcmp(comm[0], "mountpoints") == 0)) {
1368 		if (comm[1] && *comm[1] == '-' && strcmp(comm[1], "--help") == 0) {
1369 			puts(_(MOUNPOINTS_USAGE));
1370 			return EXIT_SUCCESS;
1371 		} else {
1372 			kbind_busy = 1;
1373 			rl_attempted_completion_function = NULL;
1374 			exit_code = media_menu(MEDIA_LIST);
1375 			rl_attempted_completion_function = my_rl_completion;
1376 			kbind_busy = 0;
1377 			return exit_code;
1378 		}
1379 	}
1380 
1381 	/* #### MEDIA #### */
1382 	else if (*comm[0] == 'm' && strcmp(comm[0], "media") == 0) {
1383 		if (comm[1] && *comm[1] == '-' && strcmp(comm[1], "--help") == 0) {
1384 			puts(_(MEDIA_USAGE));
1385 			return EXIT_SUCCESS;
1386 		} else {
1387 			kbind_busy = 1;
1388 			rl_attempted_completion_function = NULL;
1389 			exit_code = media_menu(MEDIA_MOUNT);
1390 			rl_attempted_completion_function = my_rl_completion;
1391 			kbind_busy = 0;
1392 			return exit_code;
1393 		}
1394 	}
1395 
1396 	/* #### MAX FILES #### */
1397 	else if (*comm[0] == 'm' && comm[0][1] == 'f' && !comm[0][2])
1398 		return set_max_files(comm);
1399 
1400 	/* #### EXT #### */
1401 	else if (*comm[0] == 'e' && comm[0][1] == 'x' && comm[0][2] == 't'
1402 	&& !comm[0][3]) {
1403 		if (!comm[1]) {
1404 			puts(_(EXT_USAGE));
1405 			return (exit_code = EXIT_FAILURE);
1406 		} else if (*comm[1] == '-' && strcmp(comm[1], "--help") == 0) {
1407 			puts(_(EXT_USAGE));
1408 		} else {
1409 			if (*comm[1] == 's' && strcmp(comm[1], "status") == 0) {
1410 				printf(_("%s: External commands %s\n"), PROGRAM_NAME,
1411 				    (ext_cmd_ok) ? _("enabled") : _("disabled"));
1412 			} else if (*comm[1] == 'o' && strcmp(comm[1], "on") == 0) {
1413 				ext_cmd_ok = 1;
1414 				printf(_("%s: External commands enabled\n"), PROGRAM_NAME);
1415 			} else if (*comm[1] == 'o' && strcmp(comm[1], "off") == 0) {
1416 				ext_cmd_ok = 0;
1417 				printf(_("%s: External commands disabled\n"), PROGRAM_NAME);
1418 			} else {
1419 				fprintf(stderr, "%s\n", _(EXT_USAGE));
1420 				exit_code = EXIT_FAILURE;
1421 			}
1422 		}
1423 		return exit_code;
1424 	}
1425 
1426 	/* #### PAGER #### */
1427 	else if (*comm[0] == 'p' && ((comm[0][1] == 'g' && !comm[0][2])
1428 	|| strcmp(comm[0], "pager") == 0)) {
1429 		if (!comm[1]) {
1430 			puts(_(PAGER_USAGE));
1431 			return (exit_code = EXIT_FAILURE);
1432 		} else if (*comm[1] == '-' && strcmp(comm[1], "--help") == 0) {
1433 			puts(_(PAGER_USAGE));
1434 			return EXIT_SUCCESS;
1435 		} else {
1436 			if (*comm[1] == 's' && strcmp(comm[1], "status") == 0) {
1437 				printf(_("%s: Pager %s\n"), PROGRAM_NAME,
1438 				    (pager) ? _("enabled") : _("disabled"));
1439 			} else if (*comm[1] == 'o' && strcmp(comm[1], "on") == 0) {
1440 				pager = 1;
1441 				printf(_("%s: Pager enabled\n"), PROGRAM_NAME);
1442 			} else if (*comm[1] == 'o' && strcmp(comm[1], "off") == 0) {
1443 				pager = 0;
1444 				printf(_("%s: Pager disabled\n"), PROGRAM_NAME);
1445 			} else {
1446 				fprintf(stderr, "%s\n", _(PAGER_USAGE));
1447 				exit_code = EXIT_FAILURE;
1448 			}
1449 		}
1450 		return exit_code;
1451 	}
1452 
1453 	/* #### FILES COUNTER #### */
1454 	else if (*comm[0] == 'f' && ((comm[0][1] == 'c' && !comm[0][2])
1455 	|| strcmp(comm[0], "filescounter") == 0)) {
1456 		if (!comm[1]) {
1457 			fprintf(stderr, "%s\n", _(FC_USAGE));
1458 			return (exit_code = EXIT_FAILURE);
1459 		}
1460 
1461 		if (*comm[1] == 'o' && strcmp(comm[1], "on") == 0) {
1462 			files_counter = 1;
1463 			puts(_("Filescounter is enabled"));
1464 			return EXIT_SUCCESS;
1465 		}
1466 
1467 		if (*comm[1] == 'o' && strcmp(comm[1], "off") == 0) {
1468 			files_counter = 0;
1469 			puts(_("Filescounter is disabled"));
1470 			return EXIT_SUCCESS;
1471 		}
1472 
1473 		if (*comm[1] == 's' && strcmp(comm[1], "status") == 0) {
1474 			if (files_counter)
1475 				puts(_("Filescounter is enabled"));
1476 			else
1477 				puts(_("Filescounter is disabled"));
1478 			return EXIT_SUCCESS;
1479 		} else {
1480 			fprintf(stderr, "%s\n", _(FC_USAGE));
1481 			return (exit_code = EXIT_FAILURE);
1482 		}
1483 	}
1484 
1485 	/* #### UNICODE #### */
1486 	else if (*comm[0] == 'u' && ((comm[0][1] == 'c' && !comm[0][2])
1487 	|| strcmp(comm[0], "unicode") == 0)) {
1488 		if (!comm[1]) {
1489 			fprintf(stderr, "%s\n", _(UNICODE_USAGE));
1490 			return (exit_code = EXIT_FAILURE);
1491 		} else if (*comm[1] == '-' && strcmp(comm[1], "--help") == 0) {
1492 			puts(_(UNICODE_USAGE));
1493 			return EXIT_SUCCESS;
1494 		} else {
1495 			if (*comm[1] == 's' && strcmp(comm[1], "status") == 0) {
1496 				printf(_("%s: Unicode %s\n"), PROGRAM_NAME,
1497 				    (unicode) ? _("enabled") : _("disabled"));
1498 			} else if (*comm[1] == 'o' && strcmp(comm[1], "on") == 0) {
1499 				unicode = 1;
1500 				printf(_("%s: Unicode enabled\n"), PROGRAM_NAME);
1501 			} else if (*comm[1] == 'o' && strcmp(comm[1], "off") == 0) {
1502 				unicode = 0;
1503 				printf(_("%s: Unicode disabled\n"), PROGRAM_NAME);
1504 			} else {
1505 				fprintf(stderr, "%s\n", _(UNICODE_USAGE));
1506 				exit_code = EXIT_FAILURE;
1507 			}
1508 		}
1509 		return exit_code;
1510 	}
1511 
1512 	/* #### FOLDERS FIRST #### */
1513 	else if (*comm[0] == 'f' && ((comm[0][1] == 'f' && !comm[0][2])
1514 	|| strcmp(comm[0], "folders-first") == 0)) {
1515 		if (autols == 0)
1516 			return EXIT_SUCCESS;
1517 		if (!comm[1]) {
1518 			fprintf(stderr, "%s\n", _(FF_USAGE));
1519 			return (exit_code = EXIT_FAILURE);
1520 		}
1521 		if (*comm[1] == '-' && strcmp(comm[1], "--help") == 0) {
1522 			puts(_(FF_USAGE));
1523 			return EXIT_SUCCESS;
1524 		}
1525 
1526 		int status = list_folders_first;
1527 		if (*comm[1] == 's' && strcmp(comm[1], "status") == 0) {
1528 			printf(_("%s: Folders first %s\n"), PROGRAM_NAME,
1529 			    (list_folders_first) ? _("enabled") : _("disabled"));
1530 		}  else if (*comm[1] == 'o' && strcmp(comm[1], "on") == 0) {
1531 			list_folders_first = 1;
1532 		} else if (*comm[1] == 'o' && strcmp(comm[1], "off") == 0) {
1533 			list_folders_first = 0;
1534 		} else {
1535 			fprintf(stderr, "%s\n", _(FF_USAGE));
1536 			return (exit_code = EXIT_FAILURE);
1537 		}
1538 
1539 		if (list_folders_first != status) {
1540 			if (autols) {
1541 				free_dirlist();
1542 				exit_code = list_dir();
1543 			}
1544 		}
1545 		return exit_code;
1546 	}
1547 
1548 	/* #### LOG #### */
1549 	else if (*comm[0] == 'l' && strcmp(comm[0], "log") == 0) {
1550 		if (comm[1] && *comm[1] == '-' && strcmp(comm[1], "--help") == 0) {
1551 			puts(_(LOG_USAGE));
1552 			return EXIT_SUCCESS;
1553 		}
1554 
1555 		/* I make this check here, and not in the function itself,
1556 		 * because this function is also called by other instances of
1557 		 * the program where no message should be printed */
1558 		if (!config_ok) {
1559 			fprintf(stderr, _("%s: Log function disabled\n"), PROGRAM_NAME);
1560 			return (exit_code = EXIT_FAILURE);
1561 		}
1562 
1563 		return (exit_code = log_function(comm));
1564 	}
1565 
1566 	/* #### MESSAGES #### */
1567 	else if (*comm[0] == 'm' && (strcmp(comm[0], "msg") == 0
1568 	|| strcmp(comm[0], "messages") == 0)) {
1569 		if (comm[1] && strcmp(comm[1], "--help") == 0) {
1570 			puts(_(MSG_USAGE));
1571 			return EXIT_SUCCESS;
1572 		}
1573 
1574 		if (comm[1] && strcmp(comm[1], "clear") == 0) {
1575 			if (!msgs_n) {
1576 				printf(_("%s: There are no messages\n"), PROGRAM_NAME);
1577 				return EXIT_SUCCESS;
1578 			}
1579 
1580 			size_t i;
1581 			for (i = 0; i < (size_t)msgs_n; i++)
1582 				free(messages[i]);
1583 
1584 			msgs_n = 0;
1585 			pmsg = NOMSG;
1586 		} else {
1587 			if (msgs_n) {
1588 				size_t i;
1589 				for (i = 0; i < (size_t)msgs_n; i++)
1590 					printf("%s", messages[i]);
1591 			} else {
1592 				printf(_("%s: There are no messages\n"), PROGRAM_NAME);
1593 			}
1594 		}
1595 		return exit_code;
1596 	}
1597 
1598 	/* #### ALIASES #### */
1599 	else if (*comm[0] == 'a' && strcmp(comm[0], "alias") == 0) {
1600 		if (comm[1]) {
1601 			if (*comm[1] == '-' && strcmp(comm[1], "--help") == 0) {
1602 				puts(_(ALIAS_USAGE));
1603 				return EXIT_SUCCESS;
1604 			} else if (*comm[1] == 'i' && strcmp(comm[1], "import") == 0) {
1605 				if (!comm[2]) {
1606 					fprintf(stderr, "%s\n", _(ALIAS_USAGE));
1607 					return (exit_code = EXIT_FAILURE);
1608 				}
1609 				return (exit_code = alias_import(comm[2]));
1610 			}
1611 		}
1612 
1613 		if (aliases_n) {
1614 			size_t i;
1615 			for (i = 0; i < aliases_n; i++)
1616 				printf("%s %s->%s %s\n", aliases[i].name, mi_c, df_c, aliases[i].cmd);
1617 		} else {
1618 			printf("%s: No aliases found\n", PROGRAM_NAME);
1619 		}
1620 		return EXIT_SUCCESS;
1621 	}
1622 
1623 	/* #### SHELL #### */
1624 /*	else if (*comm[0] == 's' && strcmp(comm[0], "shell") == 0) {
1625 		if (!comm[1]) {
1626 			if (user.shell)
1627 				printf("%s: shell: %s\n", PROGRAM_NAME, user.shell);
1628 			else
1629 				printf(_("%s: shell: unknown\n"), PROGRAM_NAME);
1630 		} else if (*comm[1] == '-' && strcmp(comm[1], "--help") == 0) {
1631 			puts(_(SHELL_USAGE));
1632 			return EXIT_SUCCESS;
1633 		} else {
1634 			return (exit_code = set_shell(comm[1]));
1635 		}
1636 	} */
1637 
1638 	/* #### EDIT #### */
1639 	else if (*comm[0] == 'e' && strcmp(comm[0], "edit") == 0)
1640 		return (exit_code = edit_function(comm));
1641 
1642 	/* #### HISTORY #### */
1643 	else if (*comm[0] == 'h' && strcmp(comm[0], "history") == 0)
1644 		return (exit_code = history_function(comm));
1645 
1646 	/* #### HIDDEN FILES #### */
1647 	else if (*comm[0] == 'h' && ((comm[0][1] == 'f' && !comm[0][2])
1648 	|| strcmp(comm[0], "hidden") == 0)) {
1649 		if (!comm[1]) {
1650 			fprintf(stderr, "%s\n", _(HF_USAGE));
1651 			return (exit_code = EXIT_FAILURE);
1652 		} else if (*comm[1] == '-' && strcmp(comm[1], "--help") == 0) {
1653 			/* The same message is in hidden_function(), and printed
1654 			 * whenever an invalid argument is entered */
1655 			puts(_(HF_USAGE));
1656 			return EXIT_SUCCESS;
1657 		} else {
1658 			return (exit_code = hidden_function(comm));
1659 		}
1660 	}
1661 
1662 	/* #### AUTOCD #### */
1663 	else if (*comm[0] == 'a' && (strcmp(comm[0], "acd") == 0
1664 	|| strcmp(comm[0], "autocd") == 0)) {
1665 		if (!comm[1]) {
1666 			fprintf(stderr, "%s\n", _(AUTOCD_USAGE));
1667 			return (exit_code = EXIT_FAILURE);
1668 		}
1669 
1670 		if (strcmp(comm[1], "on") == 0) {
1671 			autocd = 1;
1672 			printf(_("%s: autocd is enabled\n"), PROGRAM_NAME);
1673 		} else if (strcmp(comm[1], "off") == 0) {
1674 			autocd = 0;
1675 			printf(_("%s: autocd is disabled\n"), PROGRAM_NAME);
1676 		} else if (strcmp(comm[1], "status") == 0) {
1677 			if (autocd)
1678 				printf(_("%s: autocd is enabled\n"), PROGRAM_NAME);
1679 			else
1680 				printf(_("%s: autocd is disabled\n"), PROGRAM_NAME);
1681 		} else if (*comm[1] == '-' && strcmp(comm[1], "--help") == 0) {
1682 			puts(_(AUTOCD_USAGE));
1683 		} else {
1684 			fprintf(stderr, "%s\n", _(AUTOCD_USAGE));
1685 			return (exit_code = EXIT_FAILURE);
1686 		}
1687 		return EXIT_SUCCESS;
1688 	}
1689 
1690 	/* #### AUTO-OPEN #### */
1691 	else if (*comm[0] == 'a' && ((comm[0][1] == 'o' && !comm[0][2])
1692 	|| strcmp(comm[0], "auto-open") == 0)) {
1693 		if (!comm[1]) {
1694 			fprintf(stderr, "%s\n", _(AUTO_OPEN_USAGE));
1695 			return (exit_code = EXIT_FAILURE);
1696 		}
1697 
1698 		if (strcmp(comm[1], "on") == 0) {
1699 			auto_open = 1;
1700 			printf(_("%s: auto-open is enabled\n"), PROGRAM_NAME);
1701 		} else if (strcmp(comm[1], "off") == 0) {
1702 			auto_open = 0;
1703 			printf(_("%s: auto-open is disabled\n"), PROGRAM_NAME);
1704 		} else if (strcmp(comm[1], "status") == 0) {
1705 			if (auto_open)
1706 				printf(_("%s: auto-open is enabled\n"), PROGRAM_NAME);
1707 			else
1708 				printf(_("%s: auto-open is disabled\n"), PROGRAM_NAME);
1709 		} else if (strcmp(comm[1], "--help") == 0) {
1710 			puts(_(AUTO_OPEN_USAGE));
1711 		} else {
1712 			fprintf(stderr, "%s\n", _(AUTO_OPEN_USAGE));
1713 			return (exit_code = EXIT_FAILURE);
1714 		}
1715 		return EXIT_SUCCESS;
1716 	}
1717 
1718 	/* #### COMMANDS #### */
1719 	else if (*comm[0] == 'c' && (strcmp(comm[0], "cmd") == 0
1720 	|| strcmp(comm[0], "commands") == 0))
1721 		return (exit_code = list_commands());
1722 
1723 	/* #### AND THESE ONES TOO #### */
1724 	/* These functions just print stuff, so that the value of exit_code
1725 	 * is always zero, that is to say, success */
1726 	else if (strcmp(comm[0], "path") == 0 || strcmp(comm[0], "cwd") == 0) {
1727 		printf("%s\n", ws[cur_ws].path);
1728 		return EXIT_SUCCESS;
1729 	}
1730 
1731 	else if ((*comm[0] == '?' && !comm[0][1]) || strcmp(comm[0], "help") == 0) {
1732 		help_function();
1733 		return EXIT_SUCCESS;
1734 	}
1735 
1736 	else if (*comm[0] == 'c' && ((comm[0][1] == 'c' && !comm[0][2])
1737 	|| strcmp(comm[0], "colors") == 0)) {
1738 		if (comm[1] && *comm[1] == '-' && strcmp(comm[1], "--help") == 0)
1739 			puts(_(COLORS_USAGE));
1740 		else
1741 			color_codes();
1742 		return EXIT_SUCCESS;
1743 	}
1744 
1745 	else if (*comm[0] == 'v' && (strcmp(comm[0], "ver") == 0
1746 	|| strcmp(comm[0], "version") == 0)) {
1747 		version_function();
1748 		return EXIT_SUCCESS;
1749 	}
1750 
1751 	else if (*comm[0] == 'f' && comm[0][1] == 's' && !comm[0][2]) {
1752 		free_software();
1753 		return EXIT_SUCCESS;
1754 	}
1755 
1756 	else if (*comm[0] == 'b' && strcmp(comm[0], "bonus") == 0) {
1757 		bonus_function();
1758 		return EXIT_SUCCESS;
1759 	}
1760 
1761 	else if (*comm[0] == 's' && strcmp(comm[0], "splash") == 0) {
1762 		splash();
1763 		return EXIT_SUCCESS;
1764 	}
1765 
1766 	/* #### QUIT #### */
1767 	else if ((*comm[0] == 'q' && (!comm[0][1] || strcmp(comm[0], "quit") == 0))
1768 	|| (*comm[0] == 'e' && strcmp(comm[0], "exit") == 0)
1769 	|| (*comm[0] == 'Q' && !comm[0][1])) {
1770 		/* Free everything and exit */
1771 		if (*comm[0] == 'Q')
1772 			cd_on_quit = 1;
1773 		int i = (int)args_n + 1;
1774 		while (--i >= 0)
1775 			free(comm[i]);
1776 		free(comm);
1777 		exit(exit_code);
1778 	}
1779 
1780 	else {
1781 				/* ###############################
1782 				 * #     AUTOCD & AUTO-OPEN (2)   #
1783 				 * ############################### */
1784 
1785 		char *tmp = (char *)xnmalloc(strlen(comm[0]) + 1, sizeof(char));
1786 		strcpy(tmp, comm[0]);
1787 		if (strchr(tmp, '\\')) {
1788 			char *dstr = dequote_str(tmp, 0);
1789 			if (dstr) {
1790 				strcpy(tmp, dstr);
1791 				free(dstr);
1792 			}
1793 		}
1794 
1795 		if (autocd && cdpath_n && !comm[1]) {
1796 			exit_code = cd_function(tmp, CD_NO_PRINT_ERROR);
1797 			if (exit_code == EXIT_SUCCESS) {
1798 				free(tmp);
1799 				return EXIT_SUCCESS;
1800 			}
1801 		}
1802 
1803 		struct stat attr;
1804 		if (stat(tmp, &attr) == 0) {
1805 			if ((attr.st_mode & S_IFMT) == S_IFDIR) {
1806 				if (autocd)
1807 					exit_code = cd_function(tmp, CD_PRINT_ERROR);
1808 				else
1809 					fprintf(stderr, _("%s: %s: Is a directory\n"),
1810 							PROGRAM_NAME, tmp);
1811 				free(tmp);
1812 				return exit_code;
1813 			} else if (auto_open && (attr.st_mode & S_IFMT) == S_IFREG) {
1814 				/* Make sure we have not an executable file */
1815 				if (!(attr.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) {
1816 					char *cmd[] = {"open", tmp, (args_n >= 1) ? comm[1]
1817 						: NULL, (args_n >= 2) ? comm[2] : NULL, NULL};
1818 					args_n++;
1819 					exit_code = open_function(cmd);
1820 					args_n--;
1821 					free(tmp);
1822 					return exit_code;
1823 				}
1824 			}
1825 		}
1826 
1827 		free(tmp);
1828 
1829 	/* ####################################################
1830 	 * #                EXTERNAL/SHELL COMMANDS           #
1831 	 * ####################################################*/
1832 
1833 		if ((exit_code = run_shell_cmd(comm)) == EXIT_FAILURE)
1834 			return EXIT_FAILURE;
1835 	}
1836 
1837 CHECK_EVENTS:
1838 	if (!autols)
1839 		return exit_code;
1840 
1841 #ifdef LINUX_INOTIFY
1842 	if (watch)
1843 		read_inotify();
1844 #elif defined(BSD_KQUEUE)
1845 	if (watch && event_fd >= 0)
1846 		read_kqueue();
1847 #endif
1848 
1849 	return exit_code;
1850 }
1851 
1852 /* Execute chained commands (cmd1;cmd2 and/or cmd1 && cmd2). The function
1853  * is called by parse_input_str() if some non-quoted double ampersand or
1854  * semicolon is found in the input string AND at least one of these
1855  * chained commands is internal */
1856 void
exec_chained_cmds(char * cmd)1857 exec_chained_cmds(char *cmd)
1858 {
1859 	if (!cmd)
1860 		return;
1861 
1862 	size_t i = 0, cmd_len = strlen(cmd);
1863 	for (i = 0; i < cmd_len; i++) {
1864 		char *str = (char *)NULL;
1865 		size_t len = 0, cond_exec = 0;
1866 
1867 		/* Get command */
1868 		str = (char *)xcalloc(strlen(cmd) + 1, sizeof(char));
1869 		while (cmd[i] && cmd[i] != '&' && cmd[i] != ';')
1870 			str[len++] = cmd[i++];
1871 
1872 		if (!*str) {
1873 			free(str);
1874 			continue;
1875 		}
1876 
1877 		/* Should we execute conditionally? */
1878 		if (cmd[i] == '&')
1879 			cond_exec = 1;
1880 
1881 		/* Execute the command */
1882 		char **tmp_cmd = parse_input_str(str);
1883 		free(str);
1884 
1885 		if (!tmp_cmd)
1886 			continue;
1887 
1888 		int error_code = 0;
1889 		size_t j;
1890 		char **alias_cmd = check_for_alias(tmp_cmd);
1891 		if (alias_cmd) {
1892 			if (exec_cmd(alias_cmd) != 0)
1893 				error_code = 1;
1894 			for (j = 0; alias_cmd[j]; j++)
1895 				free(alias_cmd[j]);
1896 			free(alias_cmd);
1897 		} else {
1898 			if (exec_cmd(tmp_cmd) != 0)
1899 				error_code = 1;
1900 			for (j = 0; j <= args_n; j++)
1901 				free(tmp_cmd[j]);
1902 			free(tmp_cmd);
1903 		}
1904 		/* Do not continue if the execution was condtional and
1905 		 * the previous command failed */
1906 		if (cond_exec && error_code)
1907 			break;
1908 	}
1909 }
1910 
1911 void
exec_profile(void)1912 exec_profile(void)
1913 {
1914 	if (!config_ok || !profile_file)
1915 		return;
1916 
1917 	FILE *fp = fopen(profile_file, "r");
1918 	if (!fp)
1919 		return;
1920 
1921 	size_t line_size = 0;
1922 	char *line = (char *)NULL;
1923 	ssize_t line_len = 0;
1924 
1925 	while ((line_len = getline(&line, &line_size, fp)) > 0) {
1926 		/* Skip empty and commented lines */
1927 		if (!*line || *line == '\n' || *line == '#')
1928 			continue;
1929 
1930 		/* Remove trailing new line char */
1931 		if (line[line_len - 1] == '\n')
1932 			line[line_len - 1] = '\0';
1933 
1934 		if (strchr(line, '=') && !_ISDIGIT(*line)) {
1935 			create_usr_var(line);
1936 		} else if (strlen(line) != 0) {
1937 		/* Parse line and execute it */
1938 			args_n = 0;
1939 
1940 			char **cmds = parse_input_str(line);
1941 
1942 			if (cmds) {
1943 				no_log = 1;
1944 				exec_cmd(cmds);
1945 				no_log = 0;
1946 				int i = (int)args_n + 1;
1947 				while (--i >= 0)
1948 					free(cmds[i]);
1949 				free(cmds);
1950 				cmds = (char **)NULL;
1951 			}
1952 			args_n = 0;
1953 		}
1954 	}
1955 
1956 	free(line);
1957 	fclose(fp);
1958 }
1959