1 /* init.c -- functions controlling the program initialization */
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 <limits.h>
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <getopt.h>
31 #include <pwd.h>
32 #include <signal.h>
33 #include <stdio.h>
34 #include <string.h>
35 #include <sys/stat.h>
36 #include <termios.h>
37 #include <time.h>
38 #include <unistd.h>
39 #include <readline/readline.h>
40 #include <readline/history.h>
41 #ifdef __NetBSD__
42 #include <ctype.h>
43 #endif
44 
45 #include "aux.h"
46 #include "checks.h"
47 #include "config.h"
48 #include "exec.h"
49 #include "init.h"
50 #include "mime.h"
51 #include "misc.h"
52 #include "navigation.h"
53 #include "sort.h"
54 #include "string.h"
55 #include "history.h"
56 #include "file_operations.h"
57 #include "autocmds.h"
58 
59 //struct user_t user;
60 
61 /*
62  * functions
63  */
64 
65 int
get_sys_shell(void)66 get_sys_shell(void)
67 {
68 	char l[PATH_MAX] = "";
69 	ssize_t ret = readlinkat(AT_FDCWD, "/bin/sh", l, PATH_MAX);
70 
71 	if (!*l || ret == -1)
72 		return SHELL_NONE;
73 
74 	char *s = (char *)NULL;
75 	char *p = strrchr(l, '/');
76 	if (p && *(++p))
77 		s = p;
78 	else
79 		s = l;
80 
81 	if (*s == 'b' && strcmp(s, "bash") == 0)
82 		return SHELL_BASH;
83 	if (*s == 'd' && strcmp(s, "dash") == 0)
84 		return SHELL_DASH;
85 	if (*s == 'f' && strcmp(s, "fish") == 0)
86 		return SHELL_FISH;
87 	if (*s == 'z' && strcmp(s, "zsh") == 0)
88 		return SHELL_ZSH;
89 
90 	return SHELL_NONE;
91 }
92 
93 #ifndef _NO_GETTEXT
94 /* Initialize gettext for translations support */
95 int
init_gettext(void)96 init_gettext(void)
97 {
98 	char locale_dir[PATH_MAX];
99 	snprintf(locale_dir, PATH_MAX - 1, "%s/locale", data_dir
100 			? data_dir : "/usr/share");
101 	bindtextdomain(PNL, locale_dir);
102 	textdomain(PNL);
103 	return EXIT_SUCCESS;
104 
105 }
106 #endif
107 
108 int
backup_argv(int argc,char ** argv)109 backup_argv(int argc, char **argv)
110 {
111 	argc_bk = argc;
112 	argv_bk = (char **)xnmalloc((size_t)argc + 1, sizeof(char *));
113 
114 	register int i = argc;
115 	while (--i >= 0)
116 		argv_bk[i] = savestring(argv[i], strlen(argv[i]));
117 	argv_bk[argc] = (char *)NULL;
118 
119 	return EXIT_SUCCESS;
120 }
121 
122 int
init_workspaces(void)123 init_workspaces(void)
124 {
125 	ws = (struct ws_t *)xnmalloc(MAX_WS, sizeof(struct ws_t));
126 	int i = MAX_WS;
127 	while (--i >= 0) {
128 		ws[i].path = (char *)NULL;
129 //		ws[i].autocmd = 0;
130 //		ws[i].num = 0;
131 	}
132 
133 	return EXIT_SUCCESS;
134 }
135 
136 int
get_home(void)137 get_home(void)
138 {
139 	if (access(user.home, W_OK) == -1) {
140 		/* If no user's home, or if it's not writable, there won't be
141 		 * any config nor trash directory. These flags are used to
142 		 * prevent functions from trying to access any of these
143 		 * directories */
144 		home_ok = 0;
145 		config_ok = 0;
146 #ifndef _NO_TRASH
147 		trash_ok = 0;
148 #endif
149 		/* Print message: trash, bookmarks, command logs, commands
150 		 * history and program messages won't be stored */
151 		_err('e', PRINT_PROMPT, _("%s: Cannot access the home directory. "
152 				  "Trash, bookmarks, commands logs, and commands history are "
153 				  "disabled. Program messages and selected files won't be "
154 				  "persistent. Using default options\n"), PROGRAM_NAME);
155 		return EXIT_FAILURE;
156 	}
157 
158 	user_home_len = strlen(user.home);
159 	return EXIT_SUCCESS;
160 }
161 
162 int
init_history(void)163 init_history(void)
164 {
165 	/* Limit the log files size */
166 	check_file_size(log_file, max_log);
167 	check_file_size(msg_log_file, max_log);
168 
169 	/* Get history */
170 	struct stat attr;
171 	if (stat(hist_file, &attr) == 0 && attr.st_size != 0) {
172 		/* If the size condition is not included, and in case of a zero
173 		 * size file, read_history() produces malloc errors */
174 		/* Recover history from the history file */
175 		read_history(hist_file); /* This line adds more leaks to readline */
176 		/* Limit the size of the history file to max_hist lines */
177 		history_truncate_file(hist_file, max_hist);
178 	} else {
179 	/* If the history file doesn't exist, create it */
180 		FILE *hist_fp = fopen(hist_file, "w+");
181 		if (!hist_fp) {
182 			_err('w', PRINT_PROMPT, "%s: fopen: '%s': %s\n",
183 			    PROGRAM_NAME, hist_file, strerror(errno));
184 		} else {
185 			/* To avoid malloc errors in read_history(), do not
186 			 * create an empty file */
187 			fputs("edit\n", hist_fp);
188 			/* There is no need to run read_history() here, since
189 			 * the history file is still empty */
190 			fclose(hist_fp);
191 		}
192 	}
193 
194 	return EXIT_SUCCESS;
195 }
196 
197 int
set_start_path(void)198 set_start_path(void)
199 {
200 	/* Last path is overriden by positional parameters in the command line */
201 	if (restore_last_path)
202 		get_last_path();
203 
204 	if (cur_ws == UNSET)
205 		cur_ws = DEF_CUR_WS;
206 
207 	if (cur_ws > MAX_WS - 1) {
208 		cur_ws = DEF_CUR_WS;
209 		_err('w', PRINT_PROMPT, _("%s: %zu: Invalid workspace."
210 			"\nFalling back to workspace %zu\n"), PROGRAM_NAME,
211 		    cur_ws, cur_ws + 1);
212 	}
213 
214 	/* If path was not set (neither in the config file nor via command
215 	 * line nor via the RestoreLastPath option), set the default (CWD),
216 	 * and if CWD is not set, use the user's home directory, and if the
217 	 * home cannot be found either, try the root directory, and if
218 	 * there's no access to the root dir either, exit.
219 	 * Bear in mind that if you launch CliFM through a terminal emulator,
220 	 * say xterm (xterm -e clifm), xterm will run a shell, say bash, and
221 	 * the shell will read its config file. Now, if this config file
222 	 * changes the CWD, this will be the CWD for CliFM */
223 	if (!ws[cur_ws].path) {
224 		char cwd[PATH_MAX] = "";
225 		if (getcwd(cwd, sizeof(cwd)) == NULL) {}
226 
227 		if (!*cwd || strlen(cwd) == 0) {
228 			if (user_home) {
229 				ws[cur_ws].path = savestring(user_home, strlen(user_home));
230 			} else {
231 				if (access("/", R_OK | X_OK) == -1) {
232 					fprintf(stderr, "%s: /: %s\n", PROGRAM_NAME,
233 					    strerror(errno));
234 					exit(EXIT_FAILURE);
235 				} else {
236 					ws[cur_ws].path = savestring("/", 1);
237 				}
238 			}
239 		} else {
240 			ws[cur_ws].path = savestring(cwd, strlen(cwd));
241 		}
242 	}
243 
244 	/* Make path the CWD */
245 	/* If chdir(path) fails, set path to cwd, list files and print the
246 	 * error message. If no access to CWD either, exit */
247 	if (xchdir(ws[cur_ws].path, NO_TITLE) == -1) {
248 		_err('e', PRINT_PROMPT, "%s: chdir: '%s': %s\n", PROGRAM_NAME,
249 		    ws[cur_ws].path, strerror(errno));
250 
251 		char cwd[PATH_MAX] = "";
252 		if (getcwd(cwd, sizeof(cwd)) == NULL) {
253 			_err(0, NOPRINT_PROMPT, _("%s: Fatal error! Failed "
254 					"retrieving current working directory\n"), PROGRAM_NAME);
255 			exit(EXIT_FAILURE);
256 		}
257 
258 		if (ws[cur_ws].path)
259 			free(ws[cur_ws].path);
260 		ws[cur_ws].path = savestring(cwd, strlen(cwd));
261 	}
262 
263 	dir_changed = 1;
264 
265 	return EXIT_SUCCESS;
266 }
267 
268 /* Get the system data directory (usually /usr/share) */
269 void
get_data_dir(void)270 get_data_dir(void)
271 {
272 	/* First try standard values for DATADIR */
273 	char *data_dirs[] = {
274 		"/usr/share",
275 		"/usr/local/share",
276 #if defined(__HAIKU__)
277 		"/boot/system/non-packaged/data",
278 		"/boot/system/data",
279 #endif
280 		NULL };
281 
282 	struct stat attr;
283 	size_t i;
284 
285 	for (i = 0; data_dirs[i]; i++) {
286 		char tmp[PATH_MAX];
287 		snprintf(tmp, PATH_MAX - 1, "%s/%s", data_dirs[i], PNL);
288 		if (stat(tmp, &attr) == EXIT_SUCCESS) {
289 			data_dir = (char *)xrealloc(data_dir, (strlen(data_dirs[i]) + 1)
290 										* sizeof(char));
291 			strcpy(data_dir, data_dirs[i]);
292 			break;
293 		}
294 	}
295 
296 /*	if (data_dir)
297 		return; */
298 	return;
299 
300 	/* If not found, try to get DATADIR from executable's path */
301 /*	data_dir = get_cmd_path(PNL);
302 
303 	if (!data_dir)
304 		return;
305 
306 	size_t j = strlen(data_dir),
307 		   count = 0;
308 
309 	while (--j >= 0) {
310 		if (data_dir[j] == '/')
311 			count++;
312 		if (count == 2) {
313 			data_dir[j] = '\0';
314 			break;
315 		}
316 	}
317 
318 	char tmp[PATH_MAX];
319 	snprintf(tmp, PATH_MAX - 1, "%s/share/%s", data_dir, PNL);
320 	if (stat(tmp, &attr) == EXIT_SUCCESS) {
321 		snprintf(tmp, PATH_MAX - 1, "%s/share", data_dir);
322 		data_dir = (char *)xrealloc(data_dir, (strlen(tmp) + 1) * sizeof(char));
323 		strcpy(data_dir, tmp);
324 		return;
325 	} */
326 }
327 
328 void
check_env_filter(void)329 check_env_filter(void)
330 {
331 	if (_filter)
332 		return;
333 
334 	char *p = getenv("CLIFM_FILTER");
335 	if (!p)
336 		return;
337 
338 	if (*p == '!') {
339 		filter_rev = 1;
340 		p++;
341 	} else {
342 		filter_rev = 0;
343 	}
344 
345 	_filter = savestring(p, strlen(p));
346 }
347 
348 char *
get_date(void)349 get_date(void)
350 {
351 	time_t rawtime = time(NULL);
352 	struct tm tm;
353 	localtime_r(&rawtime, &tm);
354 	size_t date_max = 128;
355 
356 	char *p = (char *)malloc((date_max + 1) * sizeof(char)), *date;
357 	if (p) {
358 		date = p;
359 		p = (char *)NULL;
360 	} else {
361 		return (char *)NULL;
362 	}
363 
364 	strftime(date, date_max, "%Y-%m-%dT%T%z", &tm);
365 	return date;
366 }
367 
368 static pid_t
get_own_pid(void)369 get_own_pid(void)
370 {
371 	pid_t pid;
372 
373 	/* Get the process id */
374 	pid = getpid();
375 
376 	if (pid < 0)
377 		return 0;
378 	return pid;
379 }
380 
381 /* Returns pointer to user data struct, exits if not found */
382 struct user_t
get_user(void)383 get_user(void)
384 {
385 	struct passwd *pw;
386 	struct user_t tmp_user;
387 
388 	pw = getpwuid(geteuid());
389 	if (!pw) {
390 		_err('e', NOPRINT_PROMPT, _("%s: Cannot detect user data. Exiting early"),
391 			PROGRAM_NAME);
392 		exit(-1);
393 	}
394 
395 	tmp_user.uid = pw->pw_uid;
396 	tmp_user.gid = pw->pw_gid;
397 	char *p = getenv("HOME");
398 	if (!p)
399 		tmp_user.home = savestring(pw->pw_dir, strlen(pw->pw_dir));
400 	else
401 		tmp_user.home = savestring(p, strlen(p));
402 	tmp_user.name = savestring(pw->pw_name, strlen(pw->pw_name));
403 	tmp_user.shell = savestring(pw->pw_shell, strlen(pw->pw_shell));
404 
405 	if (!tmp_user.home || !tmp_user.name || !tmp_user.shell) {
406 		_err('e', NOPRINT_PROMPT, _("%s: Cannot detect user data. Exiting"),
407 			PROGRAM_NAME);
408 		exit(-1);
409 	}
410 
411 	tmp_user.home_len = strlen(tmp_user.home);
412 	return tmp_user;
413 }
414 
415 /* Reconstruct the jump database from database file */
416 void
load_jumpdb(void)417 load_jumpdb(void)
418 {
419 	if (xargs.no_dirjump == 1 || !config_ok || !config_dir)
420 		return;
421 
422 	size_t dir_len = strlen(config_dir);
423 	char *jump_file = (char *)xnmalloc(dir_len + 10, sizeof(char));
424 	snprintf(jump_file, dir_len + 10, "%s/jump.cfm", config_dir);
425 
426 	int fd;
427 	FILE *fp = open_fstream_r(jump_file, &fd);
428 	if (!fp) {
429 		free(jump_file);
430 		return;
431 	}
432 
433 	char tmp_line[PATH_MAX];
434 	size_t jump_lines = 0;
435 
436 	while (fgets(tmp_line, (int)sizeof(tmp_line), fp)) {
437 		if (*tmp_line != '\n' && *tmp_line >= '0' && *tmp_line <= '9')
438 			jump_lines++;
439 	}
440 
441 	if (!jump_lines) {
442 		free(jump_file);
443 		close_fstream(fp, fd);
444 		return;
445 	}
446 
447 	jump_db = (struct jump_t *)xnmalloc(jump_lines + 2, sizeof(struct jump_t));
448 
449 	fseek(fp, 0L, SEEK_SET);
450 
451 	size_t line_size = 0;
452 	char *line = (char *)NULL;
453 	ssize_t line_len = 0;
454 
455 	while ((line_len = getline(&line, &line_size, fp)) > 0) {
456 		if (!*line || *line == '\n' || *line == '#')
457 			continue;
458 		if (*line == '@') {
459 			if (line[line_len - 1] == '\n')
460 				line[line_len - 1] = '\0';
461 			if (is_number(line + 1))
462 				jump_total_rank = atoi(line + 1);
463 			continue;
464 		}
465 		if (*line < '0' || *line > '9')
466 			continue;
467 
468 		if (line[line_len - 1] == '\n')
469 			line[line_len - 1] = '\0';
470 
471 		char *tmp = strchr(line, ':');
472 		if (!tmp)
473 			continue;
474 
475 		*tmp = '\0';
476 		if (!*(++tmp))
477 			continue;
478 
479 		int visits = 1;
480 
481 		if (is_number(line))
482 			visits = atoi(line);
483 
484 		char *tmpb = strchr(tmp, ':');
485 		if (!tmpb)
486 			continue;
487 
488 		*tmpb = '\0';
489 
490 		if (!*(++tmpb))
491 			continue;
492 
493 		time_t first = 0;
494 
495 		if (is_number(tmp))
496 			first = (time_t)atoi(tmp);
497 
498 		char *tmpc = strchr(tmpb, ':');
499 		if (!tmpc)
500 			continue;
501 
502 		*tmpc = '\0';
503 
504 		if (!*(++tmpc))
505 			continue;
506 
507 		/* Purge the database from non-existent directories */
508 		if (access(tmpc, F_OK) == -1)
509 			continue;
510 
511 		jump_db[jump_n].visits = (size_t)visits;
512 		jump_db[jump_n].first_visit = first;
513 
514 		if (is_number(tmpb))
515 			jump_db[jump_n].last_visit = (time_t)atoi(tmpb);
516 		else
517 			jump_db[jump_n].last_visit = 0; /* UNIX Epoch */
518 
519 		jump_db[jump_n].keep = 0;
520 		jump_db[jump_n].rank = 0;
521 		jump_db[jump_n++].path = savestring(tmpc, strlen(tmpc));
522 	}
523 
524 	close_fstream(fp, fd);
525 	free(line);
526 	free(jump_file);
527 
528 	if (!jump_n) {
529 		free(jump_db);
530 		jump_db = (struct jump_t *)NULL;
531 		return;
532 	}
533 
534 	jump_db[jump_n].path = (char *)NULL;
535 	jump_db[jump_n].rank = 0;
536 	jump_db[jump_n].keep = 0;
537 	jump_db[jump_n].visits = 0;
538 	jump_db[jump_n].first_visit = -1;
539 }
540 
541 int
load_bookmarks(void)542 load_bookmarks(void)
543 {
544 	if (create_bm_file() == EXIT_FAILURE)
545 		return EXIT_FAILURE;
546 
547 	if (!bm_file)
548 		return EXIT_FAILURE;
549 
550 	int fd;
551 	FILE *fp = open_fstream_r(bm_file, &fd);
552 	if (!fp)
553 		return EXIT_FAILURE;
554 
555 	size_t bm_total = 0;
556 	char tmp_line[256];
557 	while (fgets(tmp_line, (int)sizeof(tmp_line), fp)) {
558 		if (!*tmp_line || *tmp_line == '#' || *tmp_line == '\n')
559 			continue;
560 		bm_total++;
561 	}
562 
563 	if (!bm_total) {
564 		close_fstream(fp, fd);
565 		return EXIT_SUCCESS;
566 	}
567 
568 	fseek(fp, 0L, SEEK_SET);
569 
570 	bookmarks = (struct bookmarks_t *)xnmalloc(bm_total + 1,
571 	    sizeof(struct bookmarks_t));
572 	size_t line_size = 0;
573 	char *line = (char *)NULL;
574 	ssize_t line_len = 0;
575 
576 	while ((line_len = getline(&line, &line_size, fp)) > 0) {
577 		if (!*line || *line == '\n' || *line == '#')
578 			continue;
579 		if (line[line_len - 1] == '\n')
580 			line[line_len - 1] = '\0';
581 
582 		/* Neither hotkey nor name, but only a path */
583 		if (*line == '/') {
584 			bookmarks[bm_n].shortcut = (char *)NULL;
585 			bookmarks[bm_n].name = (char *)NULL;
586 			bookmarks[bm_n++].path = savestring(line, strlen(line));
587 			continue;
588 		}
589 
590 		if (*line == '[') {
591 			char *p = line;
592 			p++;
593 			char *tmp = strchr(line, ']');
594 			if (!tmp) {
595 				bookmarks[bm_n].shortcut = (char *)NULL;
596 				bookmarks[bm_n].name = (char *)NULL;
597 				bookmarks[bm_n++].path = (char *)NULL;
598 				continue;
599 			}
600 
601 			*tmp = '\0';
602 
603 			bookmarks[bm_n].shortcut = savestring(p, strlen(p));
604 
605 			tmp++;
606 			p = tmp;
607 			tmp = strchr(p, ':');
608 
609 			if (!tmp) {
610 				bookmarks[bm_n].name = (char *)NULL;
611 				if (*p)
612 					bookmarks[bm_n++].path = savestring(p, strlen(p));
613 				else
614 					bookmarks[bm_n++].path = (char *)NULL;
615 				continue;
616 			}
617 
618 			*tmp = '\0';
619 			bookmarks[bm_n].name = savestring(p, strlen(p));
620 
621 			if (!*(++tmp)) {
622 				bookmarks[bm_n++].path = (char *)NULL;
623 				continue;
624 			}
625 
626 			bookmarks[bm_n++].path = savestring(tmp, strlen(tmp));
627 			continue;
628 		}
629 
630 		/* No shortcut. Let's try with name */
631 		bookmarks[bm_n].shortcut = (char *)NULL;
632 		char *tmp = strchr(line, ':');
633 
634 		/* No name either */
635 		if (!tmp) {
636 			bookmarks[bm_n].name = (char *)NULL;
637 			bookmarks[bm_n++].path = (char *)NULL;
638 			continue;
639 		}
640 
641 		*tmp = '\0';
642 		bookmarks[bm_n].name = savestring(line, strlen(line));
643 
644 		if (!*(++tmp)) {
645 			bookmarks[bm_n++].path = (char *)NULL;
646 			continue;
647 		} else {
648 			bookmarks[bm_n++].path = savestring(tmp, strlen(tmp));
649 		}
650 	}
651 
652 	free(line);
653 	close_fstream(fp, fd);
654 
655 	if (!bm_n) {
656 		free(bookmarks);
657 		bookmarks = (struct bookmarks_t *)NULL;
658 		return EXIT_SUCCESS;
659 	}
660 
661 	/* bookmark_names array shouldn't exist: is only used for bookmark
662 	 * completion. xbookmarks[i].name should be used instead, but is
663 	 * currently not working */
664 
665 	size_t i, j = 0;
666 	bookmark_names = (char **)xnmalloc(bm_n + 2, sizeof(char *));
667 
668 	for (i = 0; i < bm_n; i++) {
669 		if (!bookmarks[i].name || !*bookmarks[i].name)
670 			continue;
671 		bookmark_names[j++] = savestring(bookmarks[i].name,
672 		    strlen(bookmarks[i].name));
673 	}
674 
675 	bookmark_names[j] = (char *)NULL;
676 	return EXIT_SUCCESS;
677 }
678 
679 /* Store actions from the actions file into a struct */
680 int
load_actions(void)681 load_actions(void)
682 {
683 	if (!config_ok)
684 		return EXIT_FAILURE;
685 
686 	/* Free the actions struct array */
687 	if (actions_n) {
688 		int i = (int)actions_n;
689 		while (--i >= 0) {
690 			free(usr_actions[i].name);
691 			free(usr_actions[i].value);
692 		}
693 
694 		free(usr_actions);
695 		usr_actions = (struct actions_t *)xnmalloc(1, sizeof(struct actions_t));
696 		actions_n = 0;
697 	}
698 
699 	/* Open the actions file */
700 	int fd;
701 	FILE *fp = open_fstream_r(actions_file, &fd);
702 	if (!fp)
703 		return EXIT_FAILURE;
704 
705 	size_t line_size = 0;
706 	char *line = (char *)NULL;
707 	ssize_t line_len = 0;
708 
709 	while ((line_len = getline(&line, &line_size, fp)) > 0) {
710 		if (!line || !*line || *line == '#' || *line == '\n')
711 			continue;
712 		if (line[line_len - 1] == '\n')
713 			line[line_len - 1] = '\0';
714 
715 		char *tmp = (char *)NULL;
716 		tmp = strrchr(line, '=');
717 		if (!tmp)
718 			continue;
719 
720 		/* Now copy left and right value of each action into the
721 		 * actions struct */
722 		usr_actions = xrealloc(usr_actions, (size_t)(actions_n + 1)
723 								* sizeof(struct actions_t));
724 		usr_actions[actions_n].value = savestring(tmp + 1, strlen(tmp + 1));
725 		*tmp = '\0';
726 		usr_actions[actions_n++].name = savestring(line, strlen(line));
727 	}
728 
729 	free(line);
730 	close_fstream(fp, fd);
731 	return EXIT_SUCCESS;
732 }
733 
734 static inline void
reset_remotes_values(const size_t i)735 reset_remotes_values(const size_t i)
736 {
737 	remotes[i].name = (char *)NULL;
738 	remotes[i].desc = (char *)NULL;
739 	remotes[i].mountpoint = (char *)NULL;
740 	remotes[i].mount_cmd = (char *)NULL;
741 	remotes[i].unmount_cmd = (char *)NULL;
742 	remotes[i].auto_unmount = 0;
743 	remotes[i].auto_mount = 0;
744 	remotes[i].mounted = 0;
745 }
746 
747 /* Load remotes information from FILE */
748 int
load_remotes(void)749 load_remotes(void)
750 {
751 	if (!remotes_file || !*remotes_file)
752 		return EXIT_FAILURE;
753 
754 	int fd;
755 	FILE *fp = open_fstream_r(remotes_file, &fd);
756 	if (!fp) {
757 		fprintf(stderr, "%s: %s\n", remotes_file, strerror(errno));
758 		return EXIT_FAILURE;
759 	}
760 
761 	size_t n = 0;
762 	remotes = (struct remote_t *)xnmalloc(n + 1, sizeof(struct remote_t));
763 	reset_remotes_values(n);
764 
765 	size_t line_sz = 0;
766 	char *line = (char *)NULL;
767 
768 	while (getline(&line, &line_sz, fp) > 0) {
769 		if (!*line || *line == '#' || *line == '\n')
770 			continue;
771 		if (*line == '[') {
772 			if (remotes[n].name)
773 				n++;
774 			remotes = (struct remote_t *)xrealloc(
775 					remotes, (n + 2) * sizeof(struct remote_t));
776 			reset_remotes_values(n);
777 
778 			char *name = strbtw(line, '[', ']');
779 			if (!name)
780 				continue;
781 			if (!*name) {
782 				free(name);
783 				name = (char *)NULL;
784 				continue;
785 			}
786 			remotes[n].name = (char *)xrealloc(remotes[n].name,
787 							(strlen(name) + 1) * sizeof(char));
788 			strcpy(remotes[n].name, name);
789 			free(name);
790 			name = (char *)NULL;
791 		}
792 
793 		if (!remotes[n].name)
794 			continue;
795 
796 		char *ret = strchr(line, '=');
797 		if (!ret)
798 			continue;
799 		if (!*(++ret))
800 			continue;
801 
802 		size_t ret_len = strlen(ret);
803 		if (ret[ret_len - 1] == '\n')
804 			ret[--ret_len] = '\0';
805 
806 		char *deq_str = remove_quotes(ret);
807 		if (deq_str)
808 			ret = deq_str;
809 
810 		if (strncmp(line, "Comment=", 8) == 0) {
811 			remotes[n].desc = (char *)xrealloc(remotes[n].desc,
812 							(ret_len + 1) * sizeof(char));
813 			strcpy(remotes[n].desc, ret);
814 		} else if (strncmp(line, "Mountpoint=", 11) == 0) {
815 			char *tmp = (char *)NULL;
816 			if (*ret == '~')
817 				tmp = tilde_expand(ret);
818 			remotes[n].mountpoint = (char *)xrealloc(remotes[n].mountpoint,
819 								((tmp ? strlen(tmp) : ret_len) + 1)
820 								* sizeof(char));
821 			strcpy(remotes[n].mountpoint, tmp ? tmp : ret);
822 			free(tmp);
823 			if (count_dir(remotes[n].mountpoint, CPOP) > 2)
824 				remotes[n].mounted = 1;
825 		} else if (strncmp(line, "MountCmd=", 9) == 0) {
826 			int replaced = 0;
827 			if (remotes[n].mountpoint) {
828 				char *rep = replace_substr(ret, "%m", remotes[n].mountpoint);
829 				if (rep) {
830 					remotes[n].mount_cmd = (char *)xrealloc(
831 								remotes[n].mount_cmd,
832 								(strlen(rep) + 1) * sizeof(char));
833 					strcpy(remotes[n].mount_cmd, rep);
834 					free(rep);
835 					replaced = 1;
836 				}
837 			}
838 
839 			if (!replaced) {
840 				remotes[n].mount_cmd = (char *)xrealloc(remotes[n].mount_cmd,
841 									(ret_len + 1) * sizeof(char));
842 				strcpy(remotes[n].mount_cmd, ret);
843 			}
844 		} else if (strncmp(line, "UnmountCmd=", 11) == 0) {
845 			int replaced = 0;
846 			if (remotes[n].mountpoint) {
847 				char *rep = replace_substr(ret, "%m", remotes[n].mountpoint);
848 				if (rep) {
849 					remotes[n].unmount_cmd = (char *)xrealloc(
850 							remotes[n].unmount_cmd,
851 							(strlen(rep) + 1) * sizeof(char));
852 					strcpy(remotes[n].unmount_cmd, rep);
853 					free(rep);
854 					replaced = 1;
855 				}
856 			}
857 
858 			if (!replaced) {
859 				remotes[n].unmount_cmd = (char *)xrealloc(remotes[n].unmount_cmd,
860 								(ret_len + 1) * sizeof(char));
861 				strcpy(remotes[n].unmount_cmd, ret);
862 			}
863 		} else if (strncmp(line, "AutoUnmount=", 12) == 0) {
864 			if (strcmp(ret, "true") == 0)
865 				remotes[n].auto_unmount = 1;
866 		} else if (strncmp(line, "AutoMount=", 10) == 0) {
867 			if (strcmp(ret, "true") == 0)
868 				remotes[n].auto_mount = 1;
869 		}
870 	}
871 
872 	free(line);
873 	close_fstream(fp, fd);
874 
875 	if (remotes[n].name) {
876 		++n;
877 		remotes[n].name = (char *)NULL;
878 	}
879 
880 	remotes_n = n;
881 	return EXIT_SUCCESS;
882 }
883 
884 /* Opener function: open FILENAME and exit */
885 static void
open_reg_exit(char * filename)886 open_reg_exit(char *filename)
887 {
888 	char *homedir = getenv("HOME");
889 	if (!homedir) {
890 		fprintf(stderr, "%s: Could not retrieve the home directory\n",
891 				PROGRAM_NAME);
892 		exit(EXIT_FAILURE);
893 	}
894 
895 	tmp_dir = savestring(P_tmpdir, P_tmpdir_len);
896 
897 	size_t mime_file_len = strlen(homedir) + (alt_profile
898 					? strlen(alt_profile) : 7) + 38;
899 	mime_file = (char *)xnmalloc(mime_file_len, sizeof(char));
900 	sprintf(mime_file, "%s/.config/clifm/profiles/%s/mimelist.cfm",
901 			homedir, alt_profile ? alt_profile : "default");
902 
903 	int ret = open_file(filename);
904 	exit(ret);
905 }
906 
907 /* Evaluate external arguments, if any, and change initial variables to
908  * its corresponding value */
909 void
external_arguments(int argc,char ** argv)910 external_arguments(int argc, char **argv)
911 {
912 	/* Disable automatic error messages to be able to handle them
913 	 * myself via the '?' case in the switch */
914 	opterr = optind = 0;
915 
916 	/* Link long (--option) and short options (-o) for the getopt_long
917 	 * function */
918 	static struct option longopts[] = {
919 	    {"no-hidden", no_argument, 0, 'a'},
920 	    {"show-hidden", no_argument, 0, 'A'},
921 	    {"bookmarks-file", no_argument, 0, 'b'},
922 	    {"config-file", no_argument, 0, 'c'},
923 	    {"config-dir", required_argument, 0, 'D'},
924 	    {"no-eln", no_argument, 0, 'e'},
925 	    {"no-folders-first", no_argument, 0, 'f'},
926 	    {"folders-first", no_argument, 0, 'F'},
927 	    {"pager", no_argument, 0, 'g'},
928 	    {"no-pager", no_argument, 0, 'G'},
929 	    {"help", no_argument, 0, 'h'},
930 	    {"horizontal-list", no_argument, 0, 'H'},
931 	    {"no-case-sensitive", no_argument, 0, 'i'},
932 	    {"case-sensitive", no_argument, 0, 'I'},
933 	    {"keybindings-file", no_argument, 0, 'k'},
934 	    {"no-long-view", no_argument, 0, 'l'},
935 	    {"long-view", no_argument, 0, 'L'},
936 	    {"dirhist-map", no_argument, 0, 'm'},
937 	    {"no-autols", no_argument, 0, 'o'},
938 	    {"autols", no_argument, 0, 'O'},
939 	    {"path", required_argument, 0, 'p'},
940 	    {"profile", required_argument, 0, 'P'},
941 	    {"splash", no_argument, 0, 's'},
942 	    {"stealth-mode", no_argument, 0, 'S'},
943 	    {"unicode", no_argument, 0, 'U'},
944 	    {"no-unicode", no_argument, 0, 'u'},
945 	    {"version", no_argument, 0, 'v'},
946 	    {"workspace", required_argument, 0, 'w'},
947 	    {"no-ext-cmds", no_argument, 0, 'x'},
948 	    {"light-mode", no_argument, 0, 'y'},
949 	    {"sort", required_argument, 0, 'z'},
950 
951 	    /* Only long options */
952 	    {"no-cd-auto", no_argument, 0, 0},
953 	    {"no-open-auto", no_argument, 0, 1},
954 	    {"no-restore-last-path", no_argument, 0, 2},
955 	    {"no-tips", no_argument, 0, 3},
956 	    {"disk-usage", no_argument, 0, 4},
957 	    {"no-classify", no_argument, 0, 6},
958 	    {"share-selbox", no_argument, 0, 7},
959 	    {"rl-vi-mode", no_argument, 0, 8},
960 	    {"max-dirhist", required_argument, 0, 9},
961 	    {"sort-reverse", no_argument, 0, 10},
962 	    {"no-files-counter", no_argument, 0, 11},
963 	    {"no-welcome-message", no_argument, 0, 12},
964 	    {"no-clear-screen", no_argument, 0, 13},
965 	    {"enable-logs", no_argument, 0, 15},
966 	    {"max-path", required_argument, 0, 16},
967 	    {"opener", required_argument, 0, 17},
968 	    {"expand-bookmarks", no_argument, 0, 18},
969 	    {"only-dirs", no_argument, 0, 19},
970 	    {"list-and-quit", no_argument, 0, 20},
971 	    {"color-scheme", required_argument, 0, 21},
972 	    {"cd-on-quit", no_argument, 0, 22},
973 	    {"no-dir-jumper", no_argument, 0, 23},
974 	    {"icons", no_argument, 0, 24},
975 	    {"icons-use-file-color", no_argument, 0, 25},
976 	    {"no-columns", no_argument, 0, 26},
977 	    {"no-colors", no_argument, 0, 27},
978 	    {"max-files", required_argument, 0, 28},
979 	    {"trash-as-rm", no_argument, 0, 29},
980 	    {"case-sens-dirjump", no_argument, 0, 30},
981 	    {"case-sens-path-comp", no_argument, 0, 31},
982 	    {"cwd-in-title", no_argument, 0, 32},
983 	    {"open", required_argument, 0, 33},
984 	    {"print-sel", no_argument, 0, 34},
985 	    {"no-suggestions", no_argument, 0, 35},
986 	    {"autojump", no_argument, 0, 36},
987 	    {"no-highlight", no_argument, 0, 37},
988 	    {"no-file-cap", no_argument, 0, 38},
989 	    {"no-file-ext", no_argument, 0, 39},
990 	    {"no-follow-symlink", no_argument, 0, 40},
991 		{"control-d-exits", no_argument, 0, 41},
992 		{"int-vars", no_argument, 0, 42},
993 		{"fzftab", no_argument, 0, 43},
994 		{"no-warning-prompt", no_argument, 0, 44},
995 		{"mnt-udisks2", no_argument, 0, 45},
996 	    {0, 0, 0, 0}
997 	};
998 
999 	/* Increment whenever a new (only) long option is added */
1000 	int long_opts = 45;
1001 	int optc;
1002 	/* Variables to store arguments to options (-c, -p and -P) */
1003 	char *path_value = (char *)NULL,
1004 		 *alt_profile_value = (char *)NULL,
1005 	     *alt_dir_value = (char *)NULL,
1006 	     *config_value = (char *)NULL,
1007 	     *kbinds_value = (char *)NULL,
1008 	     *bm_value = (char *)NULL;
1009 
1010 	while ((optc = getopt_long(argc, argv,
1011 		    "+aAb:c:D:efFgGhHiIk:lLmoOp:P:sSUuvw:xyz:", longopts,
1012 		    (int *)0)) != EOF) {
1013 		/* ':' and '::' in the short options string means 'required' and
1014 		 * 'optional argument' respectivelly. Thus, 'p' and 'P' require
1015 		 * an argument here. The plus char (+) tells getopt to stop
1016 		 * processing at the first non-option (and non-argument) */
1017 		switch (optc) {
1018 
1019 		case 0: xargs.autocd = autocd = 0; break;
1020 		case 1:	xargs.auto_open = auto_open = 0; break;
1021 		case 2:	xargs.restore_last_path = restore_last_path = 0; break;
1022 		case 3: xargs.tips = tips = 0; break;
1023 		case 4:	xargs.disk_usage = disk_usage = 1; break;
1024 
1025 		case 6:	xargs.classify = classify = 0; break;
1026 		case 7:	xargs.share_selbox = share_selbox = 1; break;
1027 		case 8:	xargs.rl_vi_mode = 1; break;
1028 
1029 		case 9: {
1030 			if (!is_number(optarg))
1031 				break;
1032 			int opt_int = atoi(optarg);
1033 			if (opt_int >= 0 && opt_int <= INT_MAX)
1034 				xargs.max_dirhist = max_dirhist = opt_int;
1035 		} break;
1036 
1037 		case 10: xargs.sort_reverse = sort_reverse = 1; break;
1038 		case 11: xargs.files_counter = files_counter = 0; break;
1039 		case 12: xargs.welcome_message = welcome_message = 0; break;
1040 		case 13: xargs.clear_screen = clear_screen = 0; break;
1041 
1042 		case 15: xargs.logs = logs_enabled = 1;	break;
1043 
1044 		case 16: {
1045 			if (!is_number(optarg))
1046 				break;
1047 			int opt_int = atoi(optarg);
1048 			if (opt_int >= 0 && opt_int <= INT_MAX)
1049 				xargs.max_path = max_path = opt_int;
1050 		} break;
1051 
1052 		case 17:
1053 			if (*optarg == '~') {
1054 				char *ep = tilde_expand(optarg);
1055 				if (ep) {
1056 					opener = savestring(ep, strlen(ep));
1057 					free(ep);
1058 				} else {
1059 					_err('w', PRINT_PROMPT, _("%s: Error expanding tilde. "
1060 						"Using default opener\n"), PROGRAM_NAME);
1061 				}
1062 			} else {
1063 				opener = savestring(optarg, strlen(optarg));
1064 			}
1065 			break;
1066 
1067 		case 18: xargs.expand_bookmarks = expand_bookmarks = 1; break;
1068 		case 19: xargs.only_dirs = only_dirs = 1; break;
1069 		case 20: xargs.list_and_quit = 1; break;
1070 		case 21: usr_cscheme = savestring(optarg, strlen(optarg)); break;
1071 		case 22: xargs.cd_on_quit = cd_on_quit = 1; break;
1072 		case 23: xargs.no_dirjump = 1; break;
1073 #ifndef _NO_ICONS
1074 		case 24: xargs.icons = icons = 1; break;
1075 		case 25:
1076 			xargs.icons = icons = 1;
1077 			xargs.icons_use_file_color = 1;
1078 			break;
1079 #else
1080 		case 24: /* fallthrough */
1081 		case 25:
1082 			fprintf(stderr, _("%s: icons: %s\n"), PROGRAM_NAME, _(NOT_AVAILABLE));
1083 			exit(EXIT_FAILURE);
1084 #endif
1085 		case 26:
1086 			xargs.columns = 0;
1087 			columned = 0;
1088 			break;
1089 
1090 		case 27:
1091 			xargs.colorize = 0;
1092 			colorize = 0;
1093 #ifndef _NO_HIGHLIGHT
1094 			xargs.highlight = highlight = 0;
1095 #endif
1096 			break;
1097 
1098 		case 28:
1099 			if (!is_number(optarg))
1100 				break;
1101 			int opt_int = atoi(optarg);
1102 			if (opt_int >= 0 && opt_int <= INT_MAX)
1103 				xargs.max_files = max_files = opt_int;
1104 			break;
1105 
1106 		case 29:
1107 #ifndef _NO_TRASH
1108 			xargs.trasrm = tr_as_rm = 1; break;
1109 #else
1110 		fprintf(stderr, _("%s: trash: %s\n"), PROGRAM_NAME, _(NOT_AVAILABLE));
1111 			exit(EXIT_FAILURE);
1112 #endif
1113 		case 30: xargs.case_sens_dirjump = case_sens_dirjump = 1; break;
1114 		case 31: xargs.case_sens_path_comp = case_sens_path_comp = 1; break;
1115 		case 32: xargs.cwd_in_title = 1; break;
1116 
1117 		case 33: {
1118 			struct stat attr;
1119 			if (stat(optarg, &attr) == -1) {
1120 				fprintf(stderr, "%s: %s: %s", PROGRAM_NAME, optarg,
1121 				    strerror(errno));
1122 				exit(EXIT_FAILURE);
1123 			}
1124 
1125 			if ((attr.st_mode & S_IFMT) != S_IFDIR) {
1126 				open_reg_exit(optarg);
1127 			} else {
1128 				printf(_("%s: %s: Is a directory\n"), PROGRAM_NAME, optarg);
1129 				exit(EXIT_FAILURE);
1130 			}
1131 
1132 			/*			flags |= START_PATH;
1133 			path_value = optarg;
1134 			xargs.path = 1; */
1135 		} break;
1136 
1137 		case 34: xargs.printsel = 1; break;
1138 #ifndef _NO_SUGGESTIONS
1139 		case 35: xargs.suggestions = suggestions = 0; break;
1140 #endif
1141 		case 36: xargs.autojump = autojump = 0; break;
1142 #ifndef _NO_HIGHLIGHT
1143 		case 37: xargs.highlight = highlight = 0; break;
1144 #else
1145 		case 37:
1146 			fprintf(stderr, _("%s: highlight: %s\n"), PROGRAM_NAME, _(NOT_AVAILABLE));
1147 				exit(EXIT_FAILURE);
1148 #endif /* !_NO_HIGHLIGHT */
1149 		case 38: xargs.check_cap = check_cap = 0; break;
1150 		case 39: xargs.check_ext = check_ext = 0; break;
1151 		case 40: xargs.follow_symlinks = follow_symlinks = 0; break;
1152 		case 41: xargs.control_d_exits = control_d_exits = 1; break;
1153 		case 42: xargs.int_vars = int_vars = 1; break;
1154 #ifndef _NO_FZF
1155 		case 43: {
1156 			char *p = get_cmd_path("fzf");
1157 			if (p) {
1158 				xargs.fzftab = 1;
1159 				free(p);
1160 			} else {
1161 				_err('w', PRINT_PROMPT, _("%s: FZF not found. Falling back "
1162 					"to standard TAB completion\n"), PROGRAM_NAME);
1163 			}
1164 			}
1165 			break;
1166 #else
1167 		case 43:
1168 			fprintf(stderr, _("%s: fzftab: %s\n"), PROGRAM_NAME, _(NOT_AVAILABLE));
1169 			exit(EXIT_FAILURE);
1170 #endif /* !_NO_FZF */
1171 
1172 		case 44: xargs.warning_prompt = warning_prompt = 0; break;
1173 		case 45: xargs.mount_cmd = MNT_UDISKS2; break;
1174 
1175 		case 'a':
1176 			flags &= ~HIDDEN; /* Remove HIDDEN from 'flags' */
1177 			show_hidden = xargs.hidden = 0;
1178 			break;
1179 
1180 		case 'A':
1181 			flags |= HIDDEN; /* Add HIDDEN to 'flags' */
1182 			show_hidden = xargs.hidden = 1;
1183 			break;
1184 
1185 		case 'b':
1186 			xargs.bm_file = 1;
1187 			bm_value = optarg;
1188 			break;
1189 
1190 		case 'c':
1191 			xargs.config = 1;
1192 			config_value = optarg;
1193 			break;
1194 
1195 		case 'D': alt_dir_value = optarg; break;
1196 		case 'e': xargs.noeln = no_eln = 1;	break;
1197 
1198 		case 'f':
1199 			flags &= ~FOLDERS_FIRST;
1200 			list_folders_first = xargs.ffirst = 0;
1201 			break;
1202 
1203 		case 'F':
1204 			flags |= FOLDERS_FIRST;
1205 			list_folders_first = xargs.ffirst = 1;
1206 			break;
1207 
1208 		case 'g': pager = xargs.pager = 1; break;
1209 		case 'G': pager = xargs.pager = 0; break;
1210 
1211 		case 'h':
1212 			flags |= HELP;
1213 			/* Do not display "Press any key to continue" */
1214 			flags |= EXT_HELP;
1215 			help_function();
1216 			exit(EXIT_SUCCESS);
1217 
1218 		case 'H': xargs.horizontal_list = 1; listing_mode = HORLIST; break;
1219 
1220 		case 'i':
1221 			flags &= ~CASE_SENS;
1222 			case_sensitive = xargs.sensitive = 0;
1223 			break;
1224 
1225 		case 'I':
1226 			flags |= CASE_SENS;
1227 			case_sensitive = xargs.sensitive = 1;
1228 			break;
1229 
1230 		case 'k': kbinds_value = optarg; break;
1231 		case 'l': long_view = xargs.longview = 0; break;
1232 		case 'L': long_view = xargs.longview = 1; break;
1233 		case 'm': dirhist_map = xargs.dirmap = 1; break;
1234 
1235 		case 'o':
1236 			flags &= ~AUTOLS;
1237 			autols = xargs.autols = 0;
1238 			break;
1239 
1240 		case 'O':
1241 			flags |= AUTOLS;
1242 			autols = xargs.autols = 1;
1243 			break;
1244 
1245 		case 'p':
1246 			flags |= START_PATH;
1247 			path_value = optarg;
1248 			xargs.path = 1;
1249 			break;
1250 
1251 		case 'P':
1252 			flags |= ALT_PROFILE;
1253 			alt_profile_value = optarg;
1254 			break;
1255 
1256 		case 's':
1257 			flags |= SPLASH;
1258 			splash_screen = xargs.splash = 1;
1259 			break;
1260 
1261 		case 'S': xargs.stealth_mode = 1; break;
1262 		case 'u': unicode = xargs.unicode = 0; break;
1263 		case 'U': unicode = xargs.unicode = 1; break;
1264 
1265 		case 'v':
1266 			flags |= PRINT_VERSION;
1267 			version_function();
1268 			exit(EXIT_SUCCESS);
1269 
1270 		case 'w': {
1271 			if (!is_number(optarg))
1272 				break;
1273 			int iopt = atoi(optarg);
1274 
1275 			if (iopt >= 0 && iopt <= MAX_WS)
1276 				cur_ws = iopt - 1;
1277 		} break;
1278 
1279 		case 'x': ext_cmd_ok = xargs.ext = 0; break;
1280 		case 'y': light_mode = xargs.light = 1; break;
1281 
1282 		case 'z': {
1283 			if (!is_number(optarg))
1284 				break;
1285 			int arg = atoi(optarg);
1286 			if (arg < 0 || arg > SORT_TYPES)
1287 				sort = 1;
1288 			else
1289 				sort = arg;
1290 			xargs.sort = sort;
1291 		} break;
1292 
1293 		case '?': /* If some unrecognized option was found... */
1294 
1295 			/* Options that requires an argument */
1296 			/* Short options */
1297 			switch (optopt) {
1298 			case 'b': /* fallthrough */
1299 			case 'c': /* fallthrough */
1300 			case 'k': /* fallthrough */
1301 			case 'p': /* fallthrough */
1302 			case 'P': /* fallthrough */
1303 			case 'w': /* fallthrough */
1304 			case 'z':
1305 				fprintf(stderr, _("%s: option requires an argument -- "
1306 						  "'%c'\nTry '%s --help' for more information.\n"),
1307 				    PROGRAM_NAME, optopt, PNL);
1308 				exit(EXIT_FAILURE);
1309 			}
1310 
1311 			/* Long options */
1312 			if (optopt >= 0 && optopt <= long_opts) {
1313 				fprintf(stderr, _("%s: unrecognized option '%s'\n"
1314 					"Try '%s --help' for more information.\n"),
1315 					PROGRAM_NAME, argv[optind - 1], PNL);
1316 				exit(EXIT_FAILURE);
1317 			}
1318 
1319 			/* If unknown option is printable... */
1320 			if (isprint(optopt)) {
1321 				fprintf(stderr, _("%s: unrecognized option '%c'\n"
1322 					"Try '%s --help' for more information.\n"),
1323 				    PROGRAM_NAME, optopt, PNL);
1324 			} else {
1325 				fprintf(stderr, _("%s: unknown option character '\\%x'\n"),
1326 				    PROGRAM_NAME, (unsigned int)optopt);
1327 			}
1328 
1329 			exit(EXIT_FAILURE);
1330 
1331 		default:
1332 			break;
1333 		}
1334 	}
1335 
1336 	/* Positional parameters. If a directory, use it as CliFM starting
1337 	 * path. Otherwise, open the file with the associated application
1338 	 * and exit */
1339 	int i = optind;
1340 	if (argv[i]) {
1341 		struct stat attr;
1342 		char *_exp_path = tilde_expand(argv[i]);
1343 		if (_exp_path) {
1344 			if (stat(_exp_path, &attr) == -1) {
1345 				fprintf(stderr, "%s: %s: %s", PROGRAM_NAME, _exp_path,
1346 					strerror(errno));
1347 				free(_exp_path);
1348 				exit(EXIT_FAILURE);
1349 			}
1350 			free(_exp_path);
1351 		} else {
1352 			fprintf(stderr, _("%s: Error expanding tilde\n"), PROGRAM_NAME);
1353 			exit(EXIT_FAILURE);
1354 		}
1355 
1356 		if ((attr.st_mode & S_IFMT) != S_IFDIR)
1357 			open_reg_exit(argv[i]);
1358 
1359 		flags |= START_PATH;
1360 		path_value = argv[i];
1361 		xargs.path = 1;
1362 	}
1363 
1364 	if (bm_value) {
1365 		char *bm_exp = (char *)NULL;
1366 
1367 		if (*bm_value == '~') {
1368 			bm_exp = tilde_expand(bm_value);
1369 			bm_value = bm_exp;
1370 		}
1371 
1372 		if (access(bm_value, R_OK) == -1) {
1373 			_err('e', PRINT_PROMPT, _("%s: %s: %s\n"
1374 						  "Falling back to the default bookmarks file\n"),
1375 			    PROGRAM_NAME, bm_value, strerror(errno));
1376 		} else {
1377 			alt_bm_file = savestring(bm_value, strlen(bm_value));
1378 			_err('n', PRINT_PROMPT, _("%s: Loaded alternative "
1379 						  "bookmarks file\n"), PROGRAM_NAME);
1380 		}
1381 	}
1382 
1383 	if (alt_dir_value) {
1384 		char *dir_exp = (char *)NULL;
1385 
1386 		if (*alt_dir_value == '~') {
1387 			dir_exp = tilde_expand(alt_dir_value);
1388 			alt_dir_value = dir_exp;
1389 		}
1390 
1391 		int dir_ok = 1;
1392 		struct stat attr;
1393 		if (stat(alt_dir_value, &attr) == -1) {
1394 			char *tmp_cmd[] = {"mkdir", "-p", alt_dir_value, NULL};
1395 			int ret = launch_execve(tmp_cmd, FOREGROUND, E_NOSTDERR);
1396 			if (ret != EXIT_SUCCESS) {
1397 				_err('e', PRINT_PROMPT, _("%s: %s: Cannot create directory "
1398 				"(error %d)\nFalling back to default configuration directory\n"),
1399 					PROGRAM_NAME, alt_dir_value, ret);
1400 				dir_ok = 0;
1401 			}
1402 		}
1403 
1404 		if (access(alt_dir_value, W_OK) == -1) {
1405 			if (dir_ok) {
1406 				_err('e', PRINT_PROMPT, _("%s: %s: %s\n"
1407 					"Falling back to default configuration directory\n"),
1408 					PROGRAM_NAME, alt_dir_value, strerror(errno));
1409 			}
1410 		} else {
1411 			alt_config_dir = savestring(alt_dir_value, strlen(alt_dir_value));
1412 			_err(0, PRINT_PROMPT, _("%s: %s: Using alternative "
1413 				"configuration directory\n"), PROGRAM_NAME, alt_config_dir);
1414 		}
1415 
1416 		if (dir_exp)
1417 			free(dir_exp);
1418 	}
1419 
1420 	if (kbinds_value) {
1421 		char *kbinds_exp = (char *)NULL;
1422 		if (*kbinds_value == '~') {
1423 			kbinds_exp = tilde_expand(kbinds_value);
1424 			kbinds_value = kbinds_exp;
1425 		}
1426 
1427 		/*      if (alt_kbinds_file) {
1428 			free(alt_kbinds_file);
1429 			alt_kbinds_file = (char *)NULL;
1430 		} */
1431 
1432 		if (access(kbinds_value, R_OK) == -1) {
1433 			_err('e', PRINT_PROMPT, _("%s: %s: %s\n"
1434 						  "Falling back to the default keybindings file\n"),
1435 			    PROGRAM_NAME, kbinds_value, strerror(errno));
1436 			/*          xargs.config = -1; */
1437 		} else {
1438 			alt_kbinds_file = savestring(kbinds_value, strlen(kbinds_value));
1439 			_err('n', PRINT_PROMPT, _("%s: Loaded alternative "
1440 				"keybindings file\n"), PROGRAM_NAME);
1441 		}
1442 
1443 		if (kbinds_exp)
1444 			free(kbinds_exp);
1445 	}
1446 
1447 	if (xargs.config && config_value) {
1448 		char *config_exp = (char *)NULL;
1449 
1450 		if (*config_value == '~') {
1451 			config_exp = tilde_expand(config_value);
1452 			config_value = config_exp;
1453 		}
1454 
1455 		/*      if (alt_config_file) {
1456 			free(alt_config_file);
1457 			alt_config_file = (char *)NULL;
1458 		} */
1459 
1460 		if (access(config_value, R_OK) == -1) {
1461 			_err('e', PRINT_PROMPT, _("%s: %s: %s\n"
1462 				"Falling back to default\n"), PROGRAM_NAME,
1463 			    config_value, strerror(errno));
1464 			xargs.config = -1;
1465 		} else {
1466 			alt_config_file = savestring(config_value, strlen(config_value));
1467 			_err('n', PRINT_PROMPT, _("%s: Loaded alternative "
1468 				"configuration file\n"), PROGRAM_NAME);
1469 		}
1470 
1471 		if (config_exp)
1472 			free(config_exp);
1473 	}
1474 
1475 	if ((flags & START_PATH) && path_value) {
1476 		char *path_exp = (char *)NULL;
1477 		char path_tmp[PATH_MAX];
1478 
1479 		if (*path_value == '~') {
1480 			path_exp = tilde_expand(path_value);
1481 			xstrsncpy(path_tmp, path_exp, PATH_MAX);
1482 		} else if (*path_value != '/') {
1483 			snprintf(path_tmp, PATH_MAX - 1, "%s/%s", getenv("PWD"), path_value);
1484 		} else {
1485 			xstrsncpy(path_tmp, path_value, PATH_MAX);
1486 		}
1487 
1488 		if (xchdir(path_tmp, SET_TITLE) == 0) {
1489 			if (cur_ws == UNSET)
1490 				cur_ws = DEF_CUR_WS;
1491 			if (ws[cur_ws].path)
1492 				free(ws[cur_ws].path);
1493 
1494 			ws[cur_ws].path = savestring(path_tmp, strlen(path_tmp));
1495 		} else { /* Error changing directory */
1496 			if (xargs.list_and_quit == 1) {
1497 				fprintf(stderr, "%s: %s: %s\n", PROGRAM_NAME,
1498 				    path_tmp, strerror(errno));
1499 				exit(EXIT_FAILURE);
1500 			}
1501 
1502 			_err('w', PRINT_PROMPT, "%s: %s: %s\n", PROGRAM_NAME,
1503 			    path_tmp, strerror(errno));
1504 		}
1505 
1506 		if (path_exp)
1507 			free(path_exp);
1508 	}
1509 
1510 	if ((flags & ALT_PROFILE) && alt_profile_value) {
1511 		if (alt_profile)
1512 			free(alt_profile);
1513 		alt_profile = savestring(alt_profile_value, strlen(alt_profile_value));
1514 	}
1515 }
1516 
1517 void
unset_xargs(void)1518 unset_xargs(void)
1519 {
1520 	xargs.auto_open = UNSET;
1521 	xargs.autocd = UNSET;
1522 	xargs.autojump = UNSET;
1523 	xargs.autols= UNSET;
1524 	xargs.bm_file = UNSET;
1525 	xargs.case_sens_dirjump = UNSET;
1526 	xargs.case_sens_path_comp = UNSET;
1527 	xargs.check_cap = UNSET;
1528 	xargs.check_ext = UNSET;
1529 	xargs.cd_on_quit = UNSET;
1530 	xargs.classify = UNSET;
1531 	xargs.clear_screen = UNSET;
1532 	xargs.color_scheme = UNSET;
1533 	xargs.config = UNSET;
1534 	xargs.control_d_exits = UNSET;
1535 	xargs.cwd_in_title = UNSET;
1536 	xargs.dirmap = UNSET;
1537 	xargs.disk_usage = UNSET;
1538 	xargs.expand_bookmarks = UNSET;
1539 	xargs.ext = UNSET;
1540 	xargs.ffirst = UNSET;
1541 	xargs.files_counter = UNSET;
1542 	xargs.follow_symlinks = UNSET;
1543 #ifndef _NO_FZF
1544 	xargs.fzftab = UNSET;
1545 #endif
1546 	xargs.hidden = UNSET;
1547 #ifndef _NO_HIGHLIGHT
1548 	xargs.highlight = UNSET;
1549 #endif
1550 	xargs.horizontal_list = UNSET;
1551 #ifndef _NO_ICONS
1552 	xargs.icons = UNSET;
1553 	xargs.icons_use_file_color = UNSET;
1554 #endif
1555 	xargs.int_vars = UNSET;
1556 	xargs.light = UNSET;
1557 	xargs.list_and_quit = UNSET;
1558 	xargs.logs = UNSET;
1559 	xargs.longview = UNSET;
1560 	xargs.max_dirhist = UNSET;
1561 	xargs.max_path = UNSET;
1562 	xargs.mount_cmd = UNSET;
1563 	xargs.colorize = UNSET;
1564 	xargs.columns = UNSET;
1565 	xargs.no_dirjump = UNSET;
1566 	xargs.noeln = UNSET;
1567 	xargs.only_dirs = UNSET;
1568 	xargs.path = UNSET;
1569 	xargs.pager = UNSET;
1570 	xargs.printsel = UNSET;
1571 	xargs.restore_last_path = UNSET;
1572 	xargs.rl_vi_mode = UNSET;
1573 	xargs.sensitive = UNSET;
1574 	xargs.share_selbox = UNSET;
1575 	xargs.sort = UNSET;
1576 	xargs.sort_reverse = UNSET;
1577 	xargs.splash = UNSET;
1578 	xargs.stealth_mode = UNSET;
1579 #ifndef _NO_SUGGESTIONS
1580 	xargs.suggestions = UNSET;
1581 #endif
1582 	xargs.tips = UNSET;
1583 #ifndef _NO_TRASH
1584 	xargs.trasrm = UNSET;
1585 #endif
1586 	xargs.unicode = UNSET;
1587 	xargs.welcome_message = UNSET;
1588 	xargs.warning_prompt = UNSET;
1589 }
1590 
1591 /* Keep track of attributes of the shell. Make sure the shell is running
1592  * interactively as the foreground job before proceeding.
1593  * Taken from:
1594  * https://www.gnu.org/software/libc/manual/html_node/Initializing-the-Shell.html#Initializing-the-Shell
1595  * */
1596 void
init_shell(void)1597 init_shell(void)
1598 {
1599 	/* If shell is not interactive */
1600 	if (!isatty(STDIN_FILENO)) {
1601 		handle_stdin();
1602 		return;
1603 	}
1604 
1605 	/* Loop until we are in the foreground */
1606 	while (tcgetpgrp(STDIN_FILENO) != (own_pid = getpgrp()))
1607 		kill(-own_pid, SIGTTIN);
1608 
1609 	/* Ignore interactive and job-control signals */
1610 	set_signals_to_ignore();
1611 	/* Put ourselves in our own process group */
1612 	own_pid = get_own_pid();
1613 
1614 	if (flags & ROOT_USR) {
1615 		/* Make the shell pgid (process group id) equal to its pid */
1616 		/* Without the setpgid line below, the program cannot be run
1617 		 * with sudo, but it can be run nonetheless by the root user */
1618 		if (setpgid(own_pid, own_pid) < 0) {
1619 			_err(0, NOPRINT_PROMPT, "%s: setpgid: %s\n", PROGRAM_NAME,
1620 			    strerror(errno));
1621 			exit(EXIT_FAILURE);
1622 		}
1623 	}
1624 
1625 	/* Grab control of the terminal */
1626 	tcsetpgrp(STDIN_FILENO, own_pid);
1627 	/* Save default terminal attributes for shell */
1628 	tcgetattr(STDIN_FILENO, &shell_tmodes);
1629 	return;
1630 }
1631 
1632 /* Get current entries in the Selection Box, if any. */
1633 int
get_sel_files(void)1634 get_sel_files(void)
1635 {
1636 	if (!selfile_ok || !config_ok || !sel_file)
1637 		return EXIT_FAILURE;
1638 
1639 	/* First, clear the sel array, in case it was already used */
1640 	if (sel_n > 0) {
1641 		int i = (int)sel_n;
1642 		while (--i >= 0)
1643 			free(sel_elements[i]);
1644 	}
1645 
1646 	/*  free(sel_elements); */
1647 
1648 	sel_n = 0;
1649 
1650 	/* Open the tmp sel file and load its contents into the sel array */
1651 	int fd;
1652 	FILE *fp = open_fstream_r(sel_file, &fd);
1653 	/*  sel_elements = xcalloc(1, sizeof(char *)); */
1654 	if (!fp)
1655 		return EXIT_FAILURE;
1656 
1657 	struct stat a;
1658 	/* Since this file contains only paths, we can be sure no line
1659 	 * length will be larger than PATH_MAX */
1660 	char line[PATH_MAX] = "";
1661 	while (fgets(line, (int)sizeof(line), fp)) {
1662 		size_t len = strlen(line);
1663 
1664 		if (line[len - 1] == '\n')
1665 			line[--len] = '\0';
1666 
1667 		/* Remove the ending slash: fstatat() won't take a symlink to dir as
1668 		 * a symlink (but as a dir), if the file name ends with a slash */
1669 		if (line[len - 1] == '/')
1670 			line[--len] = '\0';
1671 
1672 		if (!*line || *line == '#')
1673 			continue;
1674 
1675 		sel_elements = (char **)xrealloc(sel_elements, (sel_n + 1) * sizeof(char *));
1676 		sel_elements[sel_n] = savestring(line, len);
1677 		/* Store device and inode number to identify later selected files
1678 		 * and mark them in the files list */
1679 		if (fstatat(AT_FDCWD, line, &a, AT_SYMLINK_NOFOLLOW) != -1) {
1680 			sel_devino = (struct devino_t *)xrealloc(sel_devino, (sel_n + 1) * sizeof(struct devino_t));
1681 			sel_devino[sel_n].ino = a.st_ino;
1682 			sel_devino[sel_n].dev = a.st_dev;
1683 		}
1684 		sel_n++;
1685 	}
1686 
1687 	close_fstream(fp, fd);
1688 	return EXIT_SUCCESS;
1689 }
1690 
1691 size_t
get_cdpath(void)1692 get_cdpath(void)
1693 {
1694 	char *p = getenv("CDPATH");
1695 	if (!p || !*p)
1696 		return 0;
1697 
1698 	char *t = savestring(p, strlen(p));
1699 
1700 	/* Get each path in CDPATH */
1701 	size_t i, n = 0, len = 0;
1702 	for (i = 0; t[i]; i++) {
1703 		/* Store path in CDPATH in a tmp buffer */
1704 		char buf[PATH_MAX];
1705 		while (t[i] && t[i] != ':')
1706 			buf[len++] = t[i++];
1707 		buf[len] = '\0';
1708 
1709 		/* Make room in cdpaths for a new path */
1710 		cdpaths = (char **)xrealloc(cdpaths, (n + 2) * sizeof(char *));
1711 
1712 		/* Dump the buffer into the global cdpaths array */
1713 		cdpaths[n++] = savestring(buf, len);
1714 
1715 		len = 0;
1716 		if (!t[i])
1717 			break;
1718 	}
1719 
1720 	cdpaths[n] = (char *)NULL;
1721 
1722 	free(t);
1723 	return n;
1724 }
1725 
1726 /* Store all paths in the PATH environment variable into a globally
1727  * declared array (paths) */
1728 size_t
get_path_env(void)1729 get_path_env(void)
1730 {
1731 	size_t i = 0;
1732 	/* Get the value of the PATH env variable */
1733 	char *path_tmp = (char *)NULL;
1734 	char *ptr = getenv("PATH");
1735 	if (!ptr || !*ptr)
1736 		return 0;
1737 	path_tmp = savestring(ptr, strlen(ptr));
1738 
1739 	if (!path_tmp)
1740 		return 0;
1741 
1742 	/* Get each path in PATH */
1743 	size_t path_num = 0, length = 0;
1744 	for (i = 0; path_tmp[i]; i++) {
1745 		/* Store path in PATH in a tmp buffer */
1746 		char buf[PATH_MAX];
1747 		while (path_tmp[i] && path_tmp[i] != ':')
1748 			buf[length++] = path_tmp[i++];
1749 		buf[length] = '\0';
1750 
1751 		/* Make room in paths for a new path */
1752 		paths = (char **)xrealloc(paths, (path_num + 1) * sizeof(char *));
1753 
1754 		/* Dump the buffer into the global paths array */
1755 		paths[path_num] = savestring(buf, length);
1756 
1757 		path_num++;
1758 		length = 0;
1759 		if (!path_tmp[i])
1760 			break;
1761 	}
1762 
1763 	free(path_tmp);
1764 	return path_num;
1765 }
1766 
1767 /* Set PATH to last visited directory and CUR_WS to last used
1768  * workspace */
1769 int
get_last_path(void)1770 get_last_path(void)
1771 {
1772 	if (!config_dir)
1773 		return EXIT_FAILURE;
1774 
1775 	char *last_file = (char *)xnmalloc(strlen(config_dir) + 7, sizeof(char));
1776 	sprintf(last_file, "%s/.last", config_dir);
1777 
1778 	int fd;
1779 	FILE *fp = open_fstream_r(last_file, &fd);
1780 	if (!fp) {
1781 		free(last_file);
1782 		return EXIT_FAILURE;
1783 	}
1784 
1785 	/*  size_t i;
1786 	for (i = 0; i < MAX_WS; i++) {
1787 
1788 		if (ws[i].path) {
1789 			free(ws[i].path);
1790 			ws[i].path = (char *)NULL;
1791 		}
1792 	} */
1793 
1794 	char line[PATH_MAX] = "";
1795 	while (fgets(line, (int)sizeof(line), fp)) {
1796 		char *p = line;
1797 		if (!*p || !strchr(p, '/'))
1798 			continue;
1799 		if (!strchr(p, ':'))
1800 			continue;
1801 
1802 		size_t len = strlen(p);
1803 		if (p[len - 1] == '\n')
1804 			p[len - 1] = '\0';
1805 
1806 		int cur = 0;
1807 		if (*p == '*') {
1808 			if (!*(++p))
1809 				continue;
1810 			cur = 1;
1811 		}
1812 
1813 		int ws_n = *p - '0';
1814 		if (cur && cur_ws == UNSET)
1815 			cur_ws = ws_n;
1816 
1817 		if (ws_n >= 0 && ws_n < MAX_WS && !ws[ws_n].path)
1818 			ws[ws_n].path = savestring(p + 2, strlen(p + 2));
1819 	}
1820 
1821 	close_fstream(fp, fd);
1822 	free(last_file);
1823 	return EXIT_SUCCESS;
1824 }
1825 
1826 /* Restore pinned dir from file */
1827 int
load_pinned_dir(void)1828 load_pinned_dir(void)
1829 {
1830 	if (!config_ok)
1831 		return EXIT_FAILURE;
1832 
1833 	char *pin_file = (char *)xnmalloc(strlen(config_dir) + 6, sizeof(char));
1834 	sprintf(pin_file, "%s/.pin", config_dir);
1835 
1836 	int fd;
1837 	FILE *fp = open_fstream_r(pin_file, &fd);
1838 	if (!fp) {
1839 		free(pin_file);
1840 		return EXIT_FAILURE;
1841 	}
1842 
1843 	char line[PATH_MAX] = "";
1844 	if (fgets(line, (int)sizeof(line), fp) == NULL) {
1845 		free(pin_file);
1846 		close_fstream(fp, fd);
1847 		return EXIT_FAILURE;
1848 	}
1849 
1850 	if (!*line || !strchr(line, '/')) {
1851 		free(pin_file);
1852 		close_fstream(fp, fd);
1853 		return EXIT_FAILURE;
1854 	}
1855 
1856 	if (pinned_dir) {
1857 		free(pinned_dir);
1858 		pinned_dir = (char *)NULL;
1859 	}
1860 
1861 	pinned_dir = savestring(line, strlen(line));
1862 	close_fstream(fp, fd);
1863 	free(pin_file);
1864 	return EXIT_SUCCESS;
1865 }
1866 
1867 /* Get the list of files in PATH, plus CliFM internal commands, and send
1868  * them into an array to be read by my readline custom auto-complete
1869  * function (my_rl_completion) */
1870 void
get_path_programs(void)1871 get_path_programs(void)
1872 {
1873 	int i, l = 0, total_cmd = 0;
1874 	int *cmd_n = (int *)0;
1875 	struct dirent ***commands_bin = (struct dirent ***)NULL;
1876 
1877 	if (ext_cmd_ok) {
1878 		char cwd[PATH_MAX] = "";
1879 		if (getcwd(cwd, sizeof(cwd)) == NULL) {}
1880 
1881 		commands_bin = (struct dirent ***)xnmalloc(
1882 						path_n, sizeof(struct dirent));
1883 		cmd_n = (int *)xnmalloc(path_n, sizeof(int));
1884 
1885 		i = (int)path_n;
1886 		while (--i >= 0) {
1887 			if (!paths[i] || !*paths[i] || xchdir(paths[i], NO_TITLE) == -1) {
1888 				cmd_n[i] = 0;
1889 				continue;
1890 			}
1891 
1892 			cmd_n[i] = scandir(paths[i], &commands_bin[i],
1893 						light_mode ? NULL : skip_nonexec, xalphasort);
1894 			/* If paths[i] directory does not exist, scandir returns -1.
1895 			 * Fedora, for example, adds $HOME/bin and $HOME/.local/bin to
1896 			 * PATH disregarding if they exist or not. If paths[i] dir is
1897 			 * empty do not use it either */
1898 			if (cmd_n[i] > 0)
1899 				total_cmd += cmd_n[i];
1900 		}
1901 		xchdir(cwd, NO_TITLE);
1902 	}
1903 
1904 	/* Add internal commands */
1905 	size_t internal_cmd_n = 0;
1906 	while (internal_cmds[internal_cmd_n])
1907 		internal_cmd_n++;
1908 
1909 	bin_commands = (char **)xnmalloc((size_t)total_cmd + internal_cmd_n +
1910 					     aliases_n + actions_n + 2, sizeof(char *));
1911 
1912 	i = (int)internal_cmd_n;
1913 	while (--i >= 0)
1914 		bin_commands[l++] = savestring(internal_cmds[i],
1915 		    strlen(internal_cmds[i]));
1916 
1917 	/* Now add aliases, if any */
1918 	if (aliases_n) {
1919 		i = (int)aliases_n;
1920 		while (--i >= 0) {
1921 			bin_commands[l++] = savestring(aliases[i].name,
1922 				strlen(aliases[i].name));
1923 		}
1924 	}
1925 
1926 	/* And user defined actions too, if any */
1927 	if (actions_n) {
1928 		i = (int)actions_n;
1929 		while (--i >= 0) {
1930 			bin_commands[l++] = savestring(usr_actions[i].name,
1931 			    strlen(usr_actions[i].name));
1932 		}
1933 	}
1934 
1935 	if (ext_cmd_ok && total_cmd) {
1936 		/* And finally, add commands in PATH */
1937 		i = (int)path_n;
1938 		while (--i >= 0) {
1939 			if (!cmd_n[i] || cmd_n[i] <= 0)
1940 				continue;
1941 
1942 			int j = cmd_n[i];
1943 			while (--j >= 0) {
1944 				bin_commands[l++] = savestring(commands_bin[i][j]->d_name,
1945 					strlen(commands_bin[i][j]->d_name));
1946 				free(commands_bin[i][j]);
1947 			}
1948 
1949 			free(commands_bin[i]);
1950 		}
1951 
1952 		free(commands_bin);
1953 		free(cmd_n);
1954 	}
1955 
1956 	path_progsn = (size_t)l;
1957 	bin_commands[l] = (char *)NULL;
1958 }
1959 
1960 void
get_aliases(void)1961 get_aliases(void)
1962 {
1963 	if (!config_ok)
1964 		return;
1965 
1966 	int fd;
1967 	FILE *fp = open_fstream_r(config_file, &fd);
1968 	if (!fp) {
1969 		_err('e', PRINT_PROMPT, "%s: alias: '%s': %s\n",
1970 		    PROGRAM_NAME, config_file, strerror(errno));
1971 		return;
1972 	}
1973 
1974 
1975 	/* Free the aliases struct array */
1976 	if (aliases_n) {
1977 		int i = (int)aliases_n;
1978 		while (--i >= 0) {
1979 			free(aliases[i].name);
1980 			free(aliases[i].cmd);
1981 		}
1982 
1983 		free(aliases);
1984 		aliases = (struct alias_t *)xnmalloc(1, sizeof(struct alias_t));
1985 		aliases_n = 0;
1986 	}
1987 
1988 	char *line = (char *)NULL;
1989 	size_t line_size = 0;
1990 
1991 	while (getline(&line, &line_size, fp) > 0) {
1992 		if (*line == 'a' && strncmp(line, "alias ", 6) == 0) {
1993 			char *s = strchr(line, ' ');
1994 			if (!s || !*(++s))
1995 				continue;
1996 			char *p = strchr(s, '=');
1997 			if (!p || !*(p + 1))
1998 				continue;
1999 			*(p++) = '\0';
2000 
2001 			/* Skip duplicated aliases names */
2002 			int i = (int)aliases_n, exists = 0;
2003 			while (--i >= 0) {
2004 				if (!aliases[i].name)
2005 					continue;
2006 				if (*s == *aliases[i].name && strcmp(s, aliases[i].name) == 0) {
2007 					exists = 1;
2008 					break;
2009 				}
2010 			}
2011 			if (exists)
2012 				continue;
2013 
2014 			aliases = (struct alias_t *)xrealloc(aliases, (aliases_n + 1)
2015 						* sizeof(struct alias_t));
2016 			aliases[aliases_n].name = savestring(s, strlen(s));
2017 			if (*p == '\'')
2018 				aliases[aliases_n++].cmd = strbtw(p, '\'', '\'');
2019 			else if (*p == '"')
2020 				aliases[aliases_n++].cmd = strbtw(p, '"', '"');
2021 		}
2022 	}
2023 
2024 	free(line);
2025 	close_fstream(fp, fd);
2026 }
2027 
2028 int
load_dirhist(void)2029 load_dirhist(void)
2030 {
2031 	if (!config_ok)
2032 		return EXIT_FAILURE;
2033 
2034 	int fd;
2035 	FILE *fp = open_fstream_r(dirhist_file, &fd);
2036 	if (!fp)
2037 		return EXIT_FAILURE;
2038 
2039 	size_t dirs = 0;
2040 
2041 	char tmp_line[PATH_MAX];
2042 	while (fgets(tmp_line, (int)sizeof(tmp_line), fp))
2043 		dirs++;
2044 
2045 	if (!dirs) {
2046 		close_fstream(fp, fd);
2047 		return EXIT_SUCCESS;
2048 	}
2049 
2050 	old_pwd = (char **)xnmalloc(dirs + 2, sizeof(char *));
2051 
2052 	fseek(fp, 0L, SEEK_SET);
2053 
2054 	size_t line_size = 0;
2055 	char *line = (char *)NULL;
2056 	ssize_t line_len = 0;
2057 
2058 	dirhist_total_index = 0;
2059 
2060 	while ((line_len = getline(&line, &line_size, fp)) > 0) {
2061 		if (!line || !*line || *line == '\n')
2062 			continue;
2063 		if (line[line_len - 1] == '\n')
2064 			line[line_len - 1] = '\0';
2065 		old_pwd[dirhist_total_index] = (char *)xnmalloc((size_t)line_len + 1,
2066 										sizeof(char));
2067 		strcpy(old_pwd[dirhist_total_index++], line);
2068 	}
2069 
2070 	close_fstream(fp, fd);
2071 	old_pwd[dirhist_total_index] = (char *)NULL;
2072 	free(line);
2073 	dirhist_cur_index = dirhist_total_index - 1;
2074 	return EXIT_SUCCESS;
2075 }
2076 
2077 void
get_prompt_cmds(void)2078 get_prompt_cmds(void)
2079 {
2080 	if (!config_ok)
2081 		return;
2082 
2083 	int fd;
2084 	FILE *fp = open_fstream_r(config_file, &fd);
2085 	if (!fp) {
2086 		_err('e', PRINT_PROMPT, "%s: prompt: '%s': %s\n",
2087 		    PROGRAM_NAME, config_file, strerror(errno));
2088 		return;
2089 	}
2090 
2091 	if (prompt_cmds_n) {
2092 		size_t i;
2093 		for (i = 0; i < prompt_cmds_n; i++)
2094 			free(prompt_cmds[i]);
2095 		free(prompt_cmds);
2096 		prompt_cmds = (char **)NULL;
2097 		prompt_cmds_n = 0;
2098 	}
2099 
2100 //	int prompt_line_found = 0;
2101 	char *line = (char *)NULL;
2102 	size_t line_size = 0;
2103 	ssize_t line_len = 0;
2104 
2105 	while ((line_len = getline(&line, &line_size, fp)) > 0) {
2106 		if (*line != 'p' || strncmp(line, "promptcmd ", 10) != 0)
2107 			continue;
2108 		if (line[line_len - 1] == '\n')
2109 			line[line_len - 1] = '\0';
2110 		if (!(*line + 10))
2111 			continue;
2112 		prompt_cmds = (char **)xrealloc(prompt_cmds,
2113 		    (prompt_cmds_n + 1) * sizeof(char *));
2114 		prompt_cmds[prompt_cmds_n++] = savestring(
2115 		    line + 10, (size_t)line_len - 10);
2116 
2117 /*		if (prompt_line_found) {
2118 			if (strncmp(line, "#END OF PROMPT", 14) == 0)
2119 				break;
2120 			if (*line != '#') {
2121 				prompt_cmds = (char **)xrealloc(prompt_cmds,
2122 				    (prompt_cmds_n + 1) * sizeof(char *));
2123 				prompt_cmds[prompt_cmds_n++] = savestring(
2124 				    line, (size_t)line_len);
2125 			}
2126 		} else if (strncmp(line, "#PROMPT", 7) == 0) {
2127 			prompt_line_found = 1;
2128 		} */
2129 	}
2130 
2131 	free(line);
2132 	close_fstream(fp, fd);
2133 }
2134 
2135 /* If some option was not set, set it to the default value */
2136 void
check_options(void)2137 check_options(void)
2138 {
2139 	if (!usr_cscheme)
2140 		usr_cscheme = savestring("default", 7);
2141 
2142 	if (!fzftab_options)
2143 		fzftab_options = savestring(DEF_FZFTAB_OPTIONS, strlen(DEF_FZFTAB_OPTIONS));
2144 
2145 	if (!wprompt_str)
2146 		wprompt_str = savestring(DEF_WPROMPT_STR, strlen(DEF_WPROMPT_STR));
2147 
2148 	/* Do no override command line options */
2149 	if (xargs.cwd_in_title == UNSET)
2150 		xargs.cwd_in_title = DEF_CWD_IN_TITLE;
2151 
2152 /*	if (xargs.mount_cmd == UNSET)
2153 		xargs.mount_cmd = DEF_MOUNT_CMD; */
2154 
2155 	if (xargs.control_d_exits == UNSET)
2156 		control_d_exits = DEF_CONTROL_D_EXITS;
2157 
2158 	if (elnpad == UNSET)
2159 		elnpad = DEF_ELNPAD;
2160 
2161 	if (cp_cmd == UNSET)
2162 		cp_cmd = DEF_CP_CMD;
2163 
2164 	if (check_cap == UNSET)
2165 		check_cap = DEF_CHECK_CAP;
2166 
2167 	if (check_ext == UNSET)
2168 		check_ext = DEF_CHECK_EXT;
2169 
2170 	if (follow_symlinks == UNSET)
2171 		follow_symlinks = DEF_FOLLOW_SYMLINKS;
2172 
2173 	if (mv_cmd == UNSET)
2174 		mv_cmd = DEF_MV_CMD;
2175 
2176 	if (min_name_trim == UNSET)
2177 		min_name_trim = DEF_MIN_NAME_TRIM;
2178 
2179 	if (min_jump_rank == UNSET)
2180 		min_jump_rank = DEF_MIN_JUMP_RANK;
2181 
2182 	if (max_jump_total_rank == UNSET)
2183 		max_jump_total_rank = DEF_MAX_JUMP_TOTAL_RANK;
2184 
2185 	if (no_eln == UNSET) {
2186 		if (xargs.noeln == UNSET)
2187 			no_eln = DEF_NOELN;
2188 		else
2189 			no_eln = xargs.noeln;
2190 	}
2191 
2192 	if (prompt_style == UNSET)
2193 		prompt_style = DEF_PROMPT_STYLE;
2194 
2195 #ifndef _NO_HIGHLIGHT
2196 	if (highlight == UNSET) {
2197 		if (xargs.highlight == UNSET)
2198 			highlight = DEF_HIGHLIGHT;
2199 		else
2200 			highlight = xargs.highlight;
2201 	}
2202 #endif
2203 
2204 	if (warning_prompt == UNSET) {
2205 		if (xargs.warning_prompt == UNSET)
2206 			warning_prompt = DEF_WARNING_PROMPT;
2207 		else
2208 			warning_prompt = xargs.warning_prompt;
2209 	}
2210 
2211 	if (listing_mode == UNSET) {
2212 		if (xargs.horizontal_list == UNSET)
2213 			listing_mode = DEF_LISTING_MODE;
2214 		else
2215 			listing_mode = xargs.horizontal_list ? 1 : 0;
2216 	}
2217 
2218 #ifndef _NO_FZF
2219 	if (fzftab == UNSET) {
2220 		if (xargs.fzftab == UNSET)
2221 			fzftab = DEF_FZFTAB;
2222 		else
2223 			fzftab = xargs.fzftab;
2224 	}
2225 #endif
2226 
2227 #ifndef _NO_ICONS
2228 	if (icons == UNSET) {
2229 		if (xargs.icons == UNSET)
2230 			icons = DEF_ICONS;
2231 		else
2232 			icons = xargs.icons;
2233 	}
2234 #endif
2235 
2236 #ifndef _NO_SUGGESTIONS
2237 	if (suggestions == UNSET) {
2238 		if (xargs.suggestions == UNSET)
2239 			suggestions = DEF_SUGGESTIONS;
2240 		else
2241 			suggestions = xargs.suggestions;
2242 	}
2243 
2244 	if (!suggestion_strategy)
2245 		suggestion_strategy = savestring(DEF_SUG_STRATEGY, SUG_STRATS);
2246 
2247 	if (suggest_filetype_color == UNSET)
2248 		suggest_filetype_color = DEF_SUG_FILETYPE_COLOR;
2249 #endif
2250 
2251 	if (int_vars == UNSET) {
2252 		if (xargs.int_vars == UNSET)
2253 			int_vars = DEF_INT_VARS;
2254 		else
2255 			int_vars = xargs.int_vars;
2256 	}
2257 
2258 	if (print_selfiles == UNSET) {
2259 		if (xargs.printsel == UNSET)
2260 			print_selfiles = DEF_PRINTSEL;
2261 		else
2262 			print_selfiles = xargs.printsel;
2263 	}
2264 
2265 	if (max_printselfiles == UNSET)
2266 		max_printselfiles = DEF_MAXPRINTSEL;
2267 
2268 	if (case_sensitive == UNSET) {
2269 		if (xargs.sensitive == UNSET)
2270 			case_sensitive = DEF_CASE_SENS_LIST;
2271 		else
2272 			case_sensitive = xargs.sensitive;
2273 	}
2274 
2275 	if (case_sens_search == UNSET)
2276 		case_sens_search = DEF_CASE_SENS_SEARCH;
2277 
2278 	if (case_sens_dirjump == UNSET) {
2279 		if (xargs.case_sens_dirjump == UNSET)
2280 			case_sens_dirjump = DEF_CASE_SENS_DIRJUMP;
2281 		else
2282 			case_sens_dirjump = xargs.case_sens_dirjump;
2283 	}
2284 
2285 	if (case_sens_path_comp == UNSET) {
2286 		if (xargs.case_sens_path_comp == UNSET)
2287 			case_sens_path_comp = DEF_CASE_SENS_PATH_COMP;
2288 		else
2289 			case_sens_path_comp = xargs.case_sens_path_comp;
2290 	}
2291 
2292 #ifndef _NO_TRASH
2293 	if (tr_as_rm == UNSET) {
2294 		if (xargs.trasrm == UNSET)
2295 			tr_as_rm = DEF_TRASRM;
2296 		else
2297 			tr_as_rm = xargs.trasrm;
2298 	}
2299 #endif
2300 
2301 	if (xargs.stealth_mode == 1 && !opener)
2302 		opener = savestring(FALLBACK_OPENER, strlen(FALLBACK_OPENER));
2303 
2304 	if (only_dirs == UNSET) {
2305 		if (xargs.only_dirs == UNSET)
2306 			only_dirs = DEF_ONLY_DIRS;
2307 		else
2308 			only_dirs = xargs.only_dirs;
2309 	}
2310 
2311 	if (colorize == UNSET) {
2312 		if (xargs.colorize == UNSET)
2313 			colorize = DEF_COLORS;
2314 		else
2315 			colorize = xargs.colorize;
2316 	}
2317 
2318 	if (expand_bookmarks == UNSET) {
2319 		if (xargs.expand_bookmarks == UNSET)
2320 			expand_bookmarks = DEF_EXPAND_BOOKMARKS;
2321 		else
2322 			expand_bookmarks = xargs.expand_bookmarks;
2323 	}
2324 
2325 	if (splash_screen == UNSET) {
2326 		if (xargs.splash == UNSET)
2327 			splash_screen = DEF_SPLASH_SCREEN;
2328 		else
2329 			splash_screen = xargs.splash;
2330 	}
2331 
2332 	if (welcome_message == UNSET) {
2333 		if (xargs.welcome_message == UNSET)
2334 			welcome_message = DEF_WELCOME_MESSAGE;
2335 		else
2336 			welcome_message = xargs.welcome_message;
2337 	}
2338 
2339 	if (show_hidden == UNSET) {
2340 		if (xargs.hidden == UNSET)
2341 			show_hidden = DEF_SHOW_HIDDEN;
2342 		else
2343 			show_hidden = xargs.hidden;
2344 	}
2345 
2346 	if (files_counter == UNSET) {
2347 		if (xargs.files_counter == UNSET)
2348 			files_counter = DEF_FILES_COUNTER;
2349 		else
2350 			files_counter = xargs.files_counter;
2351 	}
2352 
2353 	if (long_view == UNSET) {
2354 		if (xargs.longview == UNSET)
2355 			long_view = DEF_LONG_VIEW;
2356 		else
2357 			long_view = xargs.longview;
2358 	}
2359 
2360 	if (ext_cmd_ok == UNSET) {
2361 		if (xargs.ext == UNSET)
2362 			ext_cmd_ok = DEF_EXT_CMD_OK;
2363 		else
2364 			ext_cmd_ok = xargs.ext;
2365 	}
2366 
2367 	if (pager == UNSET) {
2368 		if (xargs.pager == UNSET)
2369 			pager = DEF_PAGER;
2370 		else
2371 			pager = xargs.pager;
2372 	}
2373 
2374 	if (max_dirhist == UNSET) {
2375 		if (xargs.max_dirhist == UNSET)
2376 			max_dirhist = DEF_MAX_DIRHIST;
2377 		else
2378 			max_dirhist = xargs.max_dirhist;
2379 	}
2380 
2381 	if (clear_screen == UNSET) {
2382 		if (xargs.clear_screen == UNSET)
2383 			clear_screen = DEF_CLEAR_SCREEN;
2384 		else
2385 			clear_screen = xargs.clear_screen;
2386 	}
2387 
2388 	if (list_folders_first == UNSET) {
2389 		if (xargs.ffirst == UNSET)
2390 			list_folders_first = DEF_LIST_FOLDERS_FIRST;
2391 		else
2392 			list_folders_first = xargs.ffirst;
2393 	}
2394 
2395 	if (autols == UNSET) {
2396 		if (xargs.autols == UNSET)
2397 			autols = DEF_AUTOLS;
2398 		else
2399 			autols = xargs.autols;
2400 	}
2401 
2402 	if (unicode == UNSET) {
2403 		if (xargs.unicode == UNSET)
2404 			unicode = DEF_UNICODE;
2405 		else
2406 			unicode = xargs.unicode;
2407 	}
2408 
2409 	if (max_path == UNSET) {
2410 		if (xargs.max_path == UNSET)
2411 			max_path = DEF_MAX_PATH;
2412 		else
2413 			max_path = xargs.max_path;
2414 	}
2415 
2416 	if (logs_enabled == UNSET) {
2417 		if (xargs.logs == UNSET)
2418 			logs_enabled = DEF_LOGS_ENABLED;
2419 		else
2420 			logs_enabled = xargs.logs;
2421 	}
2422 
2423 	if (light_mode == UNSET) {
2424 		if (xargs.light == UNSET)
2425 			light_mode = DEF_LIGHT_MODE;
2426 		else
2427 			light_mode = xargs.light;
2428 	}
2429 
2430 	if (classify == UNSET) {
2431 		if (xargs.classify == UNSET)
2432 			classify = DEF_CLASSIFY;
2433 		else
2434 			classify = xargs.classify;
2435 	}
2436 
2437 	if (share_selbox == UNSET) {
2438 		if (xargs.share_selbox == UNSET)
2439 			share_selbox = DEF_SHARE_SELBOX;
2440 		else
2441 			share_selbox = xargs.share_selbox;
2442 	}
2443 
2444 	if (sort == UNSET) {
2445 		if (xargs.sort == UNSET)
2446 			sort = DEF_SORT;
2447 		else
2448 			sort = xargs.sort;
2449 	}
2450 
2451 	if (sort_reverse == UNSET) {
2452 		if (xargs.sort_reverse == UNSET)
2453 			sort_reverse = DEF_SORT_REVERSE;
2454 		else
2455 			sort_reverse = xargs.sort_reverse;
2456 	}
2457 
2458 	if (tips == UNSET) {
2459 		if (xargs.tips == UNSET)
2460 			tips = DEF_TIPS;
2461 		else
2462 			tips = xargs.tips;
2463 	}
2464 
2465 	if (autocd == UNSET) {
2466 		if (xargs.autocd == UNSET)
2467 			autocd = DEF_AUTOCD;
2468 		else
2469 			autocd = xargs.autocd;
2470 	}
2471 
2472 	if (auto_open == UNSET) {
2473 		if (xargs.auto_open == UNSET)
2474 			auto_open = DEF_AUTO_OPEN;
2475 		else
2476 			auto_open = xargs.auto_open;
2477 	}
2478 
2479 	if (autojump == UNSET) {
2480 		if (xargs.autojump == UNSET)
2481 			autojump = DEF_AUTOJUMP;
2482 		else
2483 			autojump = xargs.autojump;
2484 		if (autojump == 1)
2485 			autocd = 1;
2486 	}
2487 
2488 	if (cd_on_quit == UNSET) {
2489 		if (xargs.cd_on_quit == UNSET)
2490 			cd_on_quit = DEF_CD_ON_QUIT;
2491 		else
2492 			cd_on_quit = xargs.cd_on_quit;
2493 	}
2494 
2495 	if (dirhist_map == UNSET) {
2496 		if (xargs.dirmap == UNSET)
2497 			dirhist_map = DEF_DIRHIST_MAP;
2498 		else
2499 			dirhist_map = xargs.dirmap;
2500 	}
2501 
2502 	if (disk_usage == UNSET) {
2503 		if (xargs.disk_usage == UNSET)
2504 			disk_usage = DEF_DISK_USAGE;
2505 		else
2506 			disk_usage = xargs.disk_usage;
2507 	}
2508 
2509 	if (restore_last_path == UNSET) {
2510 		if (xargs.restore_last_path == UNSET)
2511 			restore_last_path = DEF_RESTORE_LAST_PATH;
2512 		else
2513 			restore_last_path = xargs.restore_last_path;
2514 	}
2515 
2516 /*	if (!*div_line_char) {
2517 		*div_line_char = DEF_DIV_LINE_CHAR;
2518 		div_line_char[1] = '\0';
2519 	} */
2520 
2521 	if (max_hist == UNSET)
2522 		max_hist = DEF_MAX_HIST;
2523 
2524 	if (max_log == UNSET)
2525 		max_log = DEF_MAX_LOG;
2526 
2527 	if (!user.shell) {
2528 		struct user_t tmp_user = get_user();
2529 		user.shell = tmp_user.shell;
2530 
2531 		/* We don't need these values of the user struct: free(d) them */
2532 		free(tmp_user.name);
2533 		free(tmp_user.home);
2534 
2535 		if (!user.shell)
2536 			user.shell = savestring(FALLBACK_SHELL, strlen(FALLBACK_SHELL));
2537 	}
2538 
2539 	if (!term)
2540 		term = savestring(DEFAULT_TERM_CMD, strlen(DEFAULT_TERM_CMD));
2541 
2542 	if (!encoded_prompt)
2543 		encoded_prompt = savestring(DEFAULT_PROMPT, strlen(DEFAULT_PROMPT));
2544 
2545 	/* Since in stealth mode we have no access to the config file, we
2546 	 * cannot use 'lira', since it relays on a file.
2547 	 * Set it thus to xdg-open, if not already set via command line */
2548 	if (xargs.stealth_mode == 1 && !opener)
2549 		opener = savestring(FALLBACK_OPENER, strlen(FALLBACK_OPENER));
2550 
2551 	reset_opts();
2552 }
2553