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