1 /* vifm
2  * Copyright (C) 2001 Ken Steen.
3  * Copyright (C) 2011 xaizek.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
18  */
19 
20 #include "config.h"
21 
22 #define HOME_EV "HOME"
23 #define VIFM_EV "VIFM"
24 #define MYVIFMRC_EV "MYVIFMRC"
25 #define TRASH "Trash"
26 #define LOG "log"
27 #define VIFMRC "vifmrc"
28 
29 #ifndef __APPLE__
30 #define SAMPLE_VIFMRC "vifmrc"
31 #define SAMPLE_MEDIAPRG "vifm-media"
32 #else
33 #define SAMPLE_VIFMRC "vifmrc-osx"
34 #define SAMPLE_MEDIAPRG "vifm-media-osx"
35 #endif
36 
37 #include <assert.h> /* assert() */
38 #include <limits.h> /* INT_MIN */
39 #include <stddef.h> /* NULL size_t */
40 #include <stdio.h> /* FILE snprintf() */
41 #include <stdlib.h> /* free() */
42 #include <string.h> /* memmove() memset() strdup() */
43 
44 #include "../compat/fs_limits.h"
45 #include "../compat/os.h"
46 #include "../int/term_title.h"
47 #include "../io/iop.h"
48 #include "../modes/dialogs/msg_dialog.h"
49 #include "../ui/color_scheme.h"
50 #include "../ui/statusbar.h"
51 #include "../ui/tabs.h"
52 #include "../ui/ui.h"
53 #include "../utils/env.h"
54 #include "../utils/file_streams.h"
55 #include "../utils/fs.h"
56 #include "../utils/log.h"
57 #include "../utils/macros.h"
58 #include "../utils/str.h"
59 #include "../utils/path.h"
60 #include "../utils/string_array.h"
61 #include "../utils/utils.h"
62 #include "../cmd_core.h"
63 #include "../filelist.h"
64 #include "../flist_hist.h"
65 #include "../marks.h"
66 #include "../opt_handlers.h"
67 #include "../status.h"
68 #include "../types.h"
69 #include "../vifm.h"
70 
71 /* Maximum supported by the implementation length of line in vifmrc file. */
72 #define MAX_VIFMRC_LINE_LEN 4*1024
73 
74 /* Default value of shell command used if $SHELL environment variable isn't
75  * set. */
76 #ifndef _WIN32
77 #define DEFAULT_SHELL_CMD "/bin/sh"
78 #else
79 #define DEFAULT_SHELL_CMD "cmd"
80 #endif
81 
82 /* Default value of the cd path list. */
83 #define DEFAULT_CD_PATH ""
84 
85 config_t cfg;
86 
87 static void find_home_dir(void);
88 static int try_home_envvar_for_home(void);
89 static int try_userprofile_envvar_for_home(void);
90 static int try_homepath_envvar_for_home(void);
91 static void find_config_dir(void);
92 static int try_vifm_envvar_for_conf(void);
93 static int try_exe_directory_for_conf(void);
94 static int try_home_envvar_for_conf(int force);
95 static int try_appdata_for_conf(void);
96 static int try_xdg_for_conf(void);
97 static void find_data_dir(void);
98 static void find_config_file(void);
99 static int try_myvifmrc_envvar_for_vifmrc(void);
100 static int try_exe_directory_for_vifmrc(void);
101 static int try_vifm_vifmrc_for_vifmrc(void);
102 static void store_config_paths(void);
103 static void setup_dirs(void);
104 static void copy_help_file(void);
105 static void create_scripts_dir(void);
106 static void copy_rc_file(void);
107 static void add_default_marks(void);
108 static int source_file_internal(strlist_t lines, const char filename[]);
109 static void show_sourcing_error(const char filename[], int line_num);
110 
111 void
cfg_init(void)112 cfg_init(void)
113 {
114 	cfg.session = NULL;
115 
116 	cfg.history_len = 15;
117 
118 	cfg.auto_execute = 0;
119 	cfg.time_format = strdup("%m/%d %H:%M");
120 	cfg.wrap_quick_view = 1;
121 	cfg.undo_levels = 100;
122 	cfg.sort_numbers = 0;
123 	cfg.follow_links = 1;
124 	cfg.fast_run = 0;
125 	cfg.confirm = CONFIRM_DELETE | CONFIRM_PERM_DELETE;
126 	cfg.vi_command = strdup("vim");
127 	cfg.vi_cmd_bg = 0;
128 	cfg.vi_x_command = strdup("");
129 	cfg.vi_x_cmd_bg = 0;
130 	cfg.use_trash = 1;
131 	cfg.use_term_multiplexer = 0;
132 	cfg.use_vim_help = 0;
133 	cfg.wild_menu = 0;
134 	cfg.wild_popup = 0;
135 
136 	cfg.sug.flags = 0;
137 	cfg.sug.maxregfiles = 5;
138 	cfg.sug.delay = 500;
139 
140 	cfg.ignore_case = 0;
141 	cfg.smart_case = 0;
142 	cfg.hl_search = 1;
143 	cfg.vifm_info = VINFO_MARKS | VINFO_BOOKMARKS;
144 	cfg.session_options = VINFO_TUI | VINFO_STATE | VINFO_TABS | VINFO_SAVEDIRS
145 	                    | VINFO_DHISTORY;
146 	cfg.scroll_off = 0;
147 	cfg.gdefault = 0;
148 	cfg.scroll_bind = 0;
149 	cfg.wrap_scan = 1;
150 	cfg.inc_search = 0;
151 	cfg.selection_is_primary = 1;
152 	cfg.tab_switches_pane = 1;
153 	cfg.use_system_calls = 0;
154 	cfg.tab_stop = 8;
155 	cfg.ruler_format = strdup("%l/%S ");
156 	cfg.status_line = strdup("");
157 
158 	cfg.lines = INT_MIN;
159 	cfg.columns = INT_MIN;
160 
161 	cfg.dot_dirs = DD_NONROOT_PARENT | DD_TREE_LEAFS_PARENT;
162 
163 	cfg.filter_inverted_by_default = 1;
164 
165 	cfg.apropos_prg = strdup("apropos %a");
166 	cfg.find_prg = strdup("find %s %a -print , "
167 			"-type d \\( ! -readable -o ! -executable \\) -prune");
168 	cfg.grep_prg = strdup("grep -n -H -I -r %i %a %s");
169 	cfg.locate_prg = strdup("locate %a");
170 	cfg.delete_prg = strdup("");
171 	cfg.media_prg = format_str("%s/" SAMPLE_MEDIAPRG, get_installed_data_dir());
172 
173 	cfg.tail_tab_line_paths = 0;
174 	cfg.trunc_normal_sb_msgs = 0;
175 	cfg.shorten_title_paths = 1;
176 	cfg.short_term_mux_titles = 0;
177 
178 	cfg.slow_fs_list = strdup("");
179 
180 	cfg.cd_path = strdup(env_get_def("CDPATH", DEFAULT_CD_PATH));
181 	replace_char(cfg.cd_path, ':', ',');
182 
183 	cfg.extra_padding = 1;
184 	cfg.side_borders_visible = 1;
185 	cfg.use_unicode_characters = 0;
186 	cfg.flexible_splitter = 1;
187 	cfg.display_statusline = 1;
188 
189 	cfg.border_filler = strdup(" ");
190 
191 	cfg.set_title = term_title_restorable();
192 
193 	cfg.chase_links = 0;
194 
195 	cfg.timeout_len = 1000;
196 	cfg.min_timeout_len = 150;
197 
198 	/* Fill cfg.word_chars as if it was initialized from isspace() function. */
199 	memset(&cfg.word_chars, 1, sizeof(cfg.word_chars));
200 	cfg.word_chars['\x00'] = 0; cfg.word_chars['\x09'] = 0;
201 	cfg.word_chars['\x0a'] = 0; cfg.word_chars['\x0b'] = 0;
202 	cfg.word_chars['\x0c'] = 0; cfg.word_chars['\x0d'] = 0;
203 	cfg.word_chars['\x20'] = 0;
204 
205 	cfg.view_dir_size = VDS_SIZE;
206 
207 	cfg.log_file[0] = '\0';
208 
209 	cfg_set_shell(env_get_def("SHELL", DEFAULT_SHELL_CMD));
210 	cfg.shell_cmd_flag = strdup((curr_stats.shell_type == ST_CMD) ? "/C" : "-c");
211 
212 	memset(&cfg.type_decs, '\0', sizeof(cfg.type_decs));
213 	cfg.type_decs[FT_DIR][DECORATION_SUFFIX][0] = '/';
214 
215 	cfg.name_decs = NULL;
216 	cfg.name_dec_count = 0;
217 
218 	cfg.fast_file_cloning = 0;
219 	cfg.cvoptions = 0;
220 
221 	cfg.case_override = 0;
222 	cfg.case_ignore = 0;
223 
224 	cfg.sizefmt.base = 1024;
225 	cfg.sizefmt.precision = 0;
226 	cfg.sizefmt.ieci_prefixes = 0;
227 	cfg.sizefmt.space = 1;
228 
229 	cfg.pane_tabs = 0;
230 	cfg.show_tab_line = STL_MULTIPLE;
231 	cfg.tab_prefix = strdup("[%N:");
232 	cfg.tab_label = strdup("");
233 	cfg.tab_suffix = strdup("]");
234 
235 	cfg.auto_ch_pos = 1;
236 	cfg.ch_pos_on = CHPOS_STARTUP | CHPOS_DIRMARK | CHPOS_ENTER;
237 }
238 
239 void
cfg_discover_paths(void)240 cfg_discover_paths(void)
241 {
242 	LOG_FUNC_ENTER;
243 
244 	find_home_dir();
245 	find_config_dir();
246 	find_data_dir();
247 	find_config_file();
248 
249 	store_config_paths();
250 
251 	setup_dirs();
252 }
253 
254 /* Tries to find home directory. */
255 static void
find_home_dir(void)256 find_home_dir(void)
257 {
258 	LOG_FUNC_ENTER;
259 
260 	if(try_home_envvar_for_home()) return;
261 	if(try_userprofile_envvar_for_home()) return;
262 	if(try_homepath_envvar_for_home()) return;
263 
264 	vifm_finish("Failed to find user's home directory.");
265 }
266 
267 /* Tries to use HOME environment variable to find home directory.  Returns
268  * non-zero on success, otherwise zero is returned. */
269 static int
try_home_envvar_for_home(void)270 try_home_envvar_for_home(void)
271 {
272 	LOG_FUNC_ENTER;
273 
274 	const char *home = env_get(HOME_EV);
275 	return home != NULL && is_dir(home);
276 }
277 
278 /* Tries to use USERPROFILE environment variable to find home directory.
279  * Returns non-zero on success, otherwise zero is returned. */
280 static int
try_userprofile_envvar_for_home(void)281 try_userprofile_envvar_for_home(void)
282 {
283 	LOG_FUNC_ENTER;
284 
285 #ifndef _WIN32
286 	return 0;
287 #else
288 	char home[PATH_MAX + 1];
289 	const char *userprofile = env_get("USERPROFILE");
290 	if(userprofile == NULL || !is_dir(userprofile))
291 		return 0;
292 	copy_str(home, sizeof(home), userprofile);
293 	system_to_internal_slashes(home);
294 	env_set(HOME_EV, home);
295 	return 1;
296 #endif
297 }
298 
299 /* Tries to use $HOMEDRIVE/$HOMEPATH as home directory.  Returns non-zero on
300  * success, otherwise zero is returned. */
301 static int
try_homepath_envvar_for_home(void)302 try_homepath_envvar_for_home(void)
303 {
304 	LOG_FUNC_ENTER;
305 
306 #ifndef _WIN32
307 	return 0;
308 #else
309 	char home[PATH_MAX + 1];
310 	const char *homedrive = env_get("HOMEDRIVE");
311 	const char *homepath = env_get("HOMEPATH");
312 	if(homedrive == NULL || !is_dir(homedrive))
313 		return 0;
314 	if(homepath == NULL || !is_dir(homepath))
315 		return 0;
316 
317 	snprintf(home, sizeof(home), "%s%s", homedrive, homepath);
318 	system_to_internal_slashes(home);
319 	env_set(HOME_EV, home);
320 	return 1;
321 #endif
322 }
323 
324 /* Tries to find configuration directory. */
325 static void
find_config_dir(void)326 find_config_dir(void)
327 {
328 	LOG_FUNC_ENTER;
329 
330 	if(try_vifm_envvar_for_conf()) return;
331 	if(try_exe_directory_for_conf()) return;
332 	if(try_home_envvar_for_conf(0)) return;
333 	if(try_appdata_for_conf()) return;
334 	if(try_xdg_for_conf()) return;
335 
336 	if(!try_home_envvar_for_conf(1))
337 	{
338 		vifm_finish("Failed to determine location of configuration files.");
339 	}
340 }
341 
342 /* Tries to use VIFM environment variable to find configuration directory.
343  * Returns non-zero on success, otherwise zero is returned. */
344 static int
try_vifm_envvar_for_conf(void)345 try_vifm_envvar_for_conf(void)
346 {
347 	LOG_FUNC_ENTER;
348 
349 	const char *vifm = env_get(VIFM_EV);
350 	return vifm != NULL && is_dir(vifm);
351 }
352 
353 /* Tries to use directory of executable file as configuration directory.
354  * Returns non-zero on success, otherwise zero is returned. */
355 static int
try_exe_directory_for_conf(void)356 try_exe_directory_for_conf(void)
357 {
358 	LOG_FUNC_ENTER;
359 
360 	char exe_dir[PATH_MAX + 1];
361 
362 	if(get_exe_dir(exe_dir, sizeof(exe_dir)) != 0)
363 	{
364 		return 0;
365 	}
366 
367 	if(!path_exists_at(exe_dir, VIFMRC, DEREF))
368 	{
369 		return 0;
370 	}
371 
372 	env_set(VIFM_EV, exe_dir);
373 	return 1;
374 }
375 
376 /* Tries to use $HOME/.vifm as configuration directory.  Tries harder on force.
377  * Returns non-zero on success, otherwise zero is returned. */
378 static int
try_home_envvar_for_conf(int force)379 try_home_envvar_for_conf(int force)
380 {
381 	LOG_FUNC_ENTER;
382 
383 	char vifm[PATH_MAX + 1];
384 
385 	const char *home = env_get(HOME_EV);
386 	if(home == NULL || !is_dir(home))
387 	{
388 		return 0;
389 	}
390 
391 	snprintf(vifm, sizeof(vifm), "%s/.vifm", home);
392 	if(!force && !is_dir(vifm))
393 	{
394 		return 0;
395 	}
396 
397 	env_set(VIFM_EV, vifm);
398 	return 1;
399 }
400 
401 /* Tries to use $APPDATA/Vifm as configuration directory.  Returns non-zero on
402  * success, otherwise zero is returned. */
403 static int
try_appdata_for_conf(void)404 try_appdata_for_conf(void)
405 {
406 	LOG_FUNC_ENTER;
407 
408 #ifndef _WIN32
409 	return 0;
410 #else
411 	char vifm[PATH_MAX + 1];
412 	const char *appdata = env_get("APPDATA");
413 	if(appdata == NULL || !is_dir(appdata))
414 		return 0;
415 	snprintf(vifm, sizeof(vifm), "%s/Vifm", appdata);
416 	system_to_internal_slashes(vifm);
417 	env_set(VIFM_EV, vifm);
418 	return 1;
419 #endif
420 }
421 
422 /* Tries to use $XDG_CONFIG_HOME/vifm as configuration directory.  Returns
423  * non-zero on success, otherwise zero is returned. */
424 static int
try_xdg_for_conf(void)425 try_xdg_for_conf(void)
426 {
427 	LOG_FUNC_ENTER;
428 
429 	char *config_dir;
430 
431 	const char *const config_home = env_get("XDG_CONFIG_HOME");
432 	if(!is_null_or_empty(config_home) && is_path_absolute(config_home))
433 	{
434 		config_dir = format_str("%s/vifm", config_home);
435 	}
436 	else if(path_exists_at(env_get(HOME_EV), ".config", DEREF))
437 	{
438 		config_dir = format_str("%s/.config/vifm", env_get(HOME_EV));
439 	}
440 	else
441 	{
442 		return 0;
443 	}
444 
445 	env_set(VIFM_EV, config_dir);
446 	free(config_dir);
447 
448 	return 1;
449 }
450 
451 /* Tries to find directory for data files. */
452 static void
find_data_dir(void)453 find_data_dir(void)
454 {
455 	LOG_FUNC_ENTER;
456 
457 	const char *const data_home = env_get("XDG_DATA_HOME");
458 	if(is_null_or_empty(data_home) || !is_path_absolute(data_home))
459 	{
460 		snprintf(cfg.data_dir, sizeof(cfg.data_dir) - 4, "%s/.local/share/",
461 				env_get(HOME_EV));
462 	}
463 	else
464 	{
465 		snprintf(cfg.data_dir, sizeof(cfg.data_dir) - 4, "%s/", data_home);
466 	}
467 
468 	strcat(cfg.data_dir, "vifm");
469 }
470 
471 /* Tries to find configuration file. */
472 static void
find_config_file(void)473 find_config_file(void)
474 {
475 	LOG_FUNC_ENTER;
476 
477 	if(try_myvifmrc_envvar_for_vifmrc()) return;
478 	if(try_exe_directory_for_vifmrc()) return;
479 	if(try_vifm_vifmrc_for_vifmrc()) return;
480 }
481 
482 /* Tries to use $MYVIFMRC as configuration file.  Returns non-zero on success,
483  * otherwise zero is returned. */
484 static int
try_myvifmrc_envvar_for_vifmrc(void)485 try_myvifmrc_envvar_for_vifmrc(void)
486 {
487 	LOG_FUNC_ENTER;
488 
489 	const char *myvifmrc = env_get(MYVIFMRC_EV);
490 	return myvifmrc != NULL && path_exists(myvifmrc, DEREF);
491 }
492 
493 /* Tries to use vifmrc in directory of executable file as configuration file.
494  * Returns non-zero on success, otherwise zero is returned. */
495 static int
try_exe_directory_for_vifmrc(void)496 try_exe_directory_for_vifmrc(void)
497 {
498 	LOG_FUNC_ENTER;
499 
500 	char exe_dir[PATH_MAX + 1];
501 	char vifmrc[PATH_MAX + 8];
502 
503 	if(get_exe_dir(exe_dir, sizeof(exe_dir)) != 0)
504 	{
505 		return 0;
506 	}
507 
508 	snprintf(vifmrc, sizeof(vifmrc), "%s/" VIFMRC, exe_dir);
509 	if(!path_exists(vifmrc, DEREF))
510 	{
511 		return 0;
512 	}
513 
514 	env_set(MYVIFMRC_EV, vifmrc);
515 	return 1;
516 }
517 
518 /* Tries to use $VIFM/vifmrc as configuration file.  Returns non-zero on
519  * success, otherwise zero is returned. */
520 static int
try_vifm_vifmrc_for_vifmrc(void)521 try_vifm_vifmrc_for_vifmrc(void)
522 {
523 	LOG_FUNC_ENTER;
524 
525 	char vifmrc[PATH_MAX + 1];
526 	const char *vifm = env_get(VIFM_EV);
527 	if(vifm == NULL || !is_dir(vifm))
528 		return 0;
529 	snprintf(vifmrc, sizeof(vifmrc), "%s/" VIFMRC, vifm);
530 	if(!path_exists(vifmrc, DEREF))
531 		return 0;
532 	env_set(MYVIFMRC_EV, vifmrc);
533 	return 1;
534 }
535 
536 /* Writes path configuration file and directories for further usage. */
537 static void
store_config_paths(void)538 store_config_paths(void)
539 {
540 	LOG_FUNC_ENTER;
541 
542 	const char *const trash_dir_fmt =
543 #ifndef _WIN32
544 			"%%r/.vifm-Trash-%%u,%s/" TRASH ",%%r/.vifm-Trash";
545 #else
546 			"%%r/.vifm-Trash,%s/" TRASH;
547 #endif
548 
549 	char *fuse_home;
550 	const char *trash_base = path_exists_at(env_get(VIFM_EV), TRASH, DEREF)
551 	                       ? cfg.config_dir
552 	                       : cfg.data_dir;
553 	const char *base = path_exists(cfg.data_dir, DEREF)
554 	                 ? cfg.data_dir
555 	                 : cfg.config_dir;
556 
557 	snprintf(cfg.home_dir, sizeof(cfg.home_dir), "%s/", env_get(HOME_EV));
558 	copy_str(cfg.config_dir, sizeof(cfg.config_dir), env_get(VIFM_EV));
559 	snprintf(cfg.colors_dir, sizeof(cfg.colors_dir), "%s/colors/",
560 			cfg.config_dir);
561 	snprintf(cfg.trash_dir, sizeof(cfg.trash_dir), trash_dir_fmt, trash_base);
562 	snprintf(cfg.log_file, sizeof(cfg.log_file), "%s/" LOG, base);
563 
564 	fuse_home = format_str("%s/fuse/", base);
565 	(void)cfg_set_fuse_home(fuse_home);
566 	free(fuse_home);
567 }
568 
569 /* Ensures existence of configuration and data directories.  Performs first run
570  * initialization. */
571 static void
setup_dirs(void)572 setup_dirs(void)
573 {
574 	LOG_FUNC_ENTER;
575 
576 	char rc_file[PATH_MAX + 8];
577 
578 	if(is_dir(cfg.config_dir))
579 	{
580 		/* We rely on this file for :help, so make sure it's there on every run. */
581 		copy_help_file();
582 
583 		create_scripts_dir();
584 		return;
585 	}
586 
587 	if(make_path(cfg.config_dir, S_IRWXU) != 0)
588 	{
589 		return;
590 	}
591 
592 	/* This must be first run of Vifm in this environment. */
593 
594 	copy_help_file();
595 	copy_rc_file();
596 	cs_write();
597 
598 	/* Ensure that just copied sample vifmrc file is used right away. */
599 	snprintf(rc_file, sizeof(rc_file), "%s/" VIFMRC, cfg.config_dir);
600 	env_set(MYVIFMRC_EV, rc_file);
601 
602 	add_default_marks();
603 }
604 
605 /* Copies help file from shared files to the ~/.vifm directory if it's not
606  * already there. */
607 static void
copy_help_file(void)608 copy_help_file(void)
609 {
610 	LOG_FUNC_ENTER;
611 
612 	char src[PATH_MAX + 16];
613 	char dst[PATH_MAX + 16];
614 
615 	io_args_t args = {
616 		.arg1.src = src,
617 		.arg2.dst = dst,
618 		.arg3.crs = IO_CRS_FAIL,
619 	};
620 
621 	snprintf(src, sizeof(src), "%s/" VIFM_HELP, get_installed_data_dir());
622 	snprintf(dst, sizeof(dst), "%s/" VIFM_HELP, cfg.config_dir);
623 
624 	/* Don't care if it fails, also don't overwrite if file exists. */
625 	(void)iop_cp(&args);
626 }
627 
628 /* Responsible for creation of scripts/ directory if it doesn't exist (in which
629  * case a README file is created as well). */
630 static void
create_scripts_dir(void)631 create_scripts_dir(void)
632 {
633 	char scripts[PATH_MAX + 16];
634 	char readme[PATH_MAX + 32];
635 	FILE *fp;
636 
637 	snprintf(scripts, sizeof(scripts), "%s/" SCRIPTS_DIR, cfg.config_dir);
638 	if(create_path(scripts, S_IRWXU) != 0)
639 	{
640 		return;
641 	}
642 
643 	snprintf(readme, sizeof(readme), "%s/README", scripts);
644 	fp = os_fopen(readme, "w");
645 	if(fp == NULL)
646 	{
647 		return;
648 	}
649 
650 	fputs("This directory is dedicated for user-supplied scripts/executables.\n"
651 				"vifm modifies its PATH environment variable to let user run those\n"
652 				"scripts without specifying full path.  All subdirectories are added\n"
653 				"as well.  File in a subdirectory overrules file with the same name\n"
654 				"in parent directories.  Restart might be needed to recognize files\n"
655 				"in newly created or renamed subdirectories.", fp);
656 
657 	fclose(fp);
658 }
659 
660 /* Copies example vifmrc file from shared files to the ~/.vifm directory. */
661 static void
copy_rc_file(void)662 copy_rc_file(void)
663 {
664 	LOG_FUNC_ENTER;
665 
666 	char src[PATH_MAX + 16];
667 	char dst[PATH_MAX + 16];
668 
669 	io_args_t args = {
670 		.arg1.src = src,
671 		.arg2.dst = dst,
672 	};
673 
674 	snprintf(src, sizeof(src), "%s/" SAMPLE_VIFMRC, get_installed_data_dir());
675 	snprintf(dst, sizeof(dst), "%s/" VIFMRC, cfg.config_dir);
676 
677 	(void)iop_cp(&args);
678 }
679 
680 /* Adds 'H' and 'z' default marks. */
681 static void
add_default_marks(void)682 add_default_marks(void)
683 {
684 	LOG_FUNC_ENTER;
685 
686 	marks_set_user(curr_view, 'H', cfg.home_dir, NO_MARK_FILE);
687 	marks_set_user(curr_view, 'z', cfg.config_dir, NO_MARK_FILE);
688 }
689 
690 void
cfg_load(void)691 cfg_load(void)
692 {
693 	const char *rc;
694 	const int prev_global_local_settings = curr_stats.global_local_settings;
695 
696 	/* Make changes of usually local settings during configuration affect all
697 	 * views. */
698 	curr_stats.global_local_settings = 1;
699 
700 	if(!vifm_testing())
701 	{
702 		/* Try to load global configuration. */
703 		(void)cfg_source_file(PACKAGE_SYSCONF_DIR "/" VIFMRC);
704 	}
705 
706 	/* Try to load local configuration. */
707 	rc = env_get(MYVIFMRC_EV);
708 	if(!is_null_or_empty(rc))
709 	{
710 		(void)cfg_source_file(rc);
711 	}
712 
713 	curr_stats.global_local_settings = prev_global_local_settings;
714 }
715 
716 int
cfg_source_file(const char filename[])717 cfg_source_file(const char filename[])
718 {
719 	/* TODO: maybe move this to commands.c or separate unit eventually. */
720 
721 	/* Binary mode is important on Windows. */
722 	FILE *fp = os_fopen(filename, "rb");
723 	if(fp == NULL)
724 	{
725 		return 1;
726 	}
727 
728 	strlist_t lines;
729 	lines.items = read_file_lines(fp, &lines.nitems);
730 	fclose(fp);
731 
732 	SourcingState sourcing_state = curr_stats.sourcing_state;
733 	curr_stats.sourcing_state = SOURCING_PROCESSING;
734 
735 	int result = source_file_internal(lines, filename);
736 
737 	curr_stats.sourcing_state = sourcing_state;
738 
739 	free_string_array(lines.items, lines.nitems);
740 	return result;
741 }
742 
743 /* Returns non-zero on error. */
744 static int
source_file_internal(strlist_t lines,const char filename[])745 source_file_internal(strlist_t lines, const char filename[])
746 {
747 	if(lines.nitems == 0)
748 	{
749 		return 0;
750 	}
751 
752 	commands_scope_start();
753 
754 	int encoutered_errors = 0;
755 
756 	char line[MAX_VIFMRC_LINE_LEN + 1];
757 	line[0] = '\0';
758 
759 	int line_num = 0;
760 	int next_line_num = 0;
761 	for(;;)
762 	{
763 		char *next_line = NULL;
764 		while(next_line_num < lines.nitems)
765 		{
766 			next_line = skip_whitespace(lines.items[next_line_num++]);
767 			if(*next_line == '"')
768 			{
769 				continue;
770 			}
771 			else if(*next_line == '\\')
772 			{
773 				strncat(line, next_line + 1, sizeof(line) - strlen(line) - 1);
774 				next_line = NULL;
775 			}
776 			else
777 			{
778 				break;
779 			}
780 		}
781 
782 		ui_sb_clear();
783 
784 		if(exec_commands(line, curr_view, CIT_COMMAND) < 0)
785 		{
786 			show_sourcing_error(filename, line_num);
787 			encoutered_errors = 1;
788 		}
789 		if(curr_stats.sourcing_state == SOURCING_FINISHING)
790 			break;
791 
792 		if(next_line == NULL)
793 		{
794 			/* Artificially increment line number to simulate as if all that happens
795 			 * after the loop relates to something past end of the file. */
796 			line_num++;
797 			break;
798 		}
799 
800 		copy_str(line, sizeof(line), next_line);
801 		line_num = next_line_num;
802 	}
803 
804 	ui_sb_clear();
805 
806 	if(commands_scope_finish() != 0)
807 	{
808 		show_sourcing_error(filename, line_num);
809 		encoutered_errors = 1;
810 	}
811 
812 	return encoutered_errors;
813 }
814 
815 /* Displays sourcing error message to a user. */
816 static void
show_sourcing_error(const char filename[],int line_num)817 show_sourcing_error(const char filename[], int line_num)
818 {
819 	const char *const last_msg = ui_sb_last();
820 
821 	curr_stats.save_msg = 1;
822 
823 	/* User choice is saved by prompt_error_msgf internally. */
824 	if(is_null_or_empty(last_msg))
825 	{
826 		(void)prompt_error_msgf("File Sourcing Error", "Error in %s at line %d",
827 				filename, line_num);
828 	}
829 	else
830 	{
831 		/* The space is needed because empty lines are automatically removed. */
832 		(void)prompt_error_msgf("File Sourcing Error", "Error in %s at line %d:\n"
833 				" \n%s", filename, line_num, last_msg);
834 	}
835 
836 	curr_stats.save_msg = 0;
837 }
838 
839 const char *
cfg_get_vicmd(int * bg)840 cfg_get_vicmd(int *bg)
841 {
842 	if(curr_stats.exec_env_type != EET_EMULATOR_WITH_X)
843 	{
844 		*bg = cfg.vi_cmd_bg;
845 		return cfg.vi_command;
846 	}
847 	else if(cfg.vi_x_command[0] != '\0')
848 	{
849 		*bg = cfg.vi_x_cmd_bg;
850 		return cfg.vi_x_command;
851 	}
852 	else
853 	{
854 		*bg = cfg.vi_cmd_bg;
855 		return cfg.vi_command;
856 	}
857 }
858 
859 void
cfg_clear_histories(int clear_dhistory)860 cfg_clear_histories(int clear_dhistory)
861 {
862 	if(clear_dhistory)
863 	{
864 		cfg_resize_histories(0);
865 	}
866 	else
867 	{
868 		hists_resize(0);
869 		cfg.history_len = 0;
870 		/* Directory histories keep their data, which will be truncated or updated
871 		 * later. */
872 	}
873 }
874 
875 void
cfg_resize_histories(int new_size)876 cfg_resize_histories(int new_size)
877 {
878 	hists_resize(new_size);
879 
880 	int i;
881 	tab_info_t tab_info;
882 	for(i = 0; tabs_enum_all(i, &tab_info); ++i)
883 	{
884 		flist_hist_resize(tab_info.view, new_size);
885 	}
886 
887 	const int old_size = cfg.history_len;
888 	cfg.history_len = new_size;
889 
890 	if(old_size <= 0 && new_size > 0)
891 	{
892 		int i;
893 		tab_info_t tab_info;
894 		for(i = 0; tabs_enum_all(i, &tab_info); ++i)
895 		{
896 			flist_hist_save(tab_info.view);
897 		}
898 	}
899 }
900 
901 int
cfg_set_fuse_home(const char new_value[])902 cfg_set_fuse_home(const char new_value[])
903 {
904 #ifdef _WIN32
905 	char with_forward_slashes[strlen(new_value) + 1];
906 	strcpy(with_forward_slashes, new_value);
907 	system_to_internal_slashes(with_forward_slashes);
908 	new_value = with_forward_slashes;
909 #endif
910 
911 	char canonicalized[PATH_MAX + 1];
912 	canonicalize_path(new_value, canonicalized, sizeof(canonicalized));
913 
914 	if(!is_path_absolute(new_value))
915 	{
916 		if(cfg.fuse_home == NULL)
917 		{
918 			/* Do not leave cfg.fuse_home uninitialized. */
919 			cfg.fuse_home = strdup("");
920 		}
921 
922 		show_error_msgf("Error Setting FUSE Home Directory",
923 				"The path is not absolute: %s", canonicalized);
924 		return 1;
925 	}
926 
927 	return replace_string(&cfg.fuse_home, canonicalized);
928 }
929 
930 void
cfg_set_use_term_multiplexer(int use_term_multiplexer)931 cfg_set_use_term_multiplexer(int use_term_multiplexer)
932 {
933 	cfg.use_term_multiplexer = use_term_multiplexer;
934 	stats_set_use_multiplexer(use_term_multiplexer);
935 }
936 
937 void
cfg_set_shell(const char shell[])938 cfg_set_shell(const char shell[])
939 {
940 	if(replace_string(&cfg.shell, shell) == 0)
941 	{
942 		stats_update_shell_type(cfg.shell);
943 	}
944 }
945 
946 int
cfg_is_word_wchar(wchar_t c)947 cfg_is_word_wchar(wchar_t c)
948 {
949 	return c >= 256 || cfg.word_chars[c];
950 }
951 
952 int
cfg_parent_dir_is_visible(int in_root)953 cfg_parent_dir_is_visible(int in_root)
954 {
955 	return ((in_root && (cfg.dot_dirs & DD_ROOT_PARENT)) ||
956 			(!in_root && (cfg.dot_dirs & DD_NONROOT_PARENT)));
957 }
958 
959 int
cfg_confirm_delete(int to_trash)960 cfg_confirm_delete(int to_trash)
961 {
962 	return to_trash
963 	     ? (cfg.confirm & CONFIRM_DELETE)
964 	     : (cfg.confirm & CONFIRM_PERM_DELETE);
965 }
966 
967 int
cfg_ch_pos_on(ChposWhen when)968 cfg_ch_pos_on(ChposWhen when)
969 {
970 	return cfg.auto_ch_pos && (cfg.ch_pos_on & when);
971 }
972 
973 /* vim: set tabstop=2 softtabstop=2 shiftwidth=2 noexpandtab cinoptions-=(0 : */
974 /* vim: set cinoptions+=t0 filetype=c : */
975