void files_chmod(FileView *view, const char *mode, int recurse_dirs) { int i; ui_cancellation_reset(); i = 0; while(i < view->list_rows && !view->dir_entry[i].selected) i++; if(i == view->list_rows) { char buf[COMMAND_GROUP_INFO_LEN]; char inv[16]; snprintf(buf, sizeof(buf), "chmod in %s: %s", replace_home_part(flist_get_dir(view)), view->dir_entry[view->list_pos].name); cmd_group_begin(buf); snprintf(inv, sizeof(inv), "0%o", view->dir_entry[view->list_pos].mode & 0xff); chmod_file_in_list(view, view->list_pos, mode, inv, recurse_dirs); } else { char buf[COMMAND_GROUP_INFO_LEN]; size_t len; int j = i; len = snprintf(buf, sizeof(buf), "chmod in %s: ", replace_home_part(flist_get_dir(view))); while(i < view->list_rows && len < sizeof(buf)) { if(view->dir_entry[i].selected) { if(len >= 2 && buf[len - 2] != ':') { strncat(buf + len, ", ", sizeof(buf) - len - 1); len += strlen(buf + len); } strncat(buf + len, view->dir_entry[i].name, sizeof(buf) - len - 1); len += strlen(buf + len); } i++; } cmd_group_begin(buf); while(j < view->list_rows && !ui_cancellation_requested()) { if(view->dir_entry[j].selected) { char inv[16]; snprintf(inv, sizeof(inv), "0%o", view->dir_entry[j].mode & 0xff); chmod_file_in_list(view, j, mode, inv, recurse_dirs); } j++; } } cmd_group_end(); }
/* Appends path to the entry to the expanded string. Returns new value of * expanded string. */ static char * append_entry(FileView *view, char expanded[], PathType type, dir_entry_t *entry, int quotes, const char mod[], int for_shell) { char path[PATH_MAX]; const char *modified; switch(type) { case PT_NAME: copy_str(path, sizeof(path), entry->name); break; case PT_REL: get_short_path_of(view, entry, 0, sizeof(path), path); break; case PT_FULL: get_full_path_of(entry, sizeof(path), path); break; default: assert(0 && "Unexpected path type"); path[0] = '\0'; break; } modified = apply_mods(path, flist_get_dir(view), mod, for_shell); expanded = append_path_to_expanded(expanded, quotes, modified); return expanded; }
void init_menu_data(menu_data_t *m, FileView *view, char title[], char empty_msg[]) { if(m->initialized) { reset_menu_data(m); } if(menu_state.d != NULL) { menu_state.d->state = NULL; } menu_state.d = m; m->top = 0; m->len = 0; m->pos = 0; m->hor_pos = 0; m->title = title; m->items = NULL; m->data = NULL; m->void_data = NULL; m->key_handler = NULL; m->extra_data = 0; m->execute_handler = NULL; m->empty_msg = empty_msg; m->cwd = strdup(flist_get_dir(view)); m->state = &menu_state; m->initialized = 1; }
/* Switches to pane tab specified by its index if the index is valid. */ static void tabs_goto_pane(int idx) { pane_tabs_t *const ptabs = get_pane_tabs(curr_view); if(ptabs->current == idx) { return; } if(idx < 0 || idx >= (int)DA_SIZE(ptabs->tabs)) { return; } ptabs->tabs[ptabs->current].view = *curr_view; assign_preview(&ptabs->tabs[ptabs->current].preview, &curr_stats.preview); *curr_view = ptabs->tabs[idx].view; assign_preview(&curr_stats.preview, &ptabs->tabs[idx].preview); ptabs->current = idx; stats_set_quickview(curr_stats.preview.on); ui_view_schedule_redraw(curr_view); load_view_options(curr_view); (void)vifm_chdir(flist_get_dir(curr_view)); }
/* Executes list of startup commands. */ static void exec_startup_commands(const args_t *args) { size_t i; for(i = 0; i < args->ncmds; ++i) { /* Make sure we're executing commands in correct directory. */ (void)vifm_chdir(flist_get_dir(curr_view)); (void)exec_commands(args->cmds[i], curr_view, CIT_COMMAND); } }
/* Single exit point for leaving vifm, performs only minimum common * deinitialization steps. */ static void _gnuc_noreturn vifm_leave(int exit_code, int cquit) { vim_write_dir(cquit ? "" : flist_get_dir(curr_view)); if(cquit && exit_code == EXIT_SUCCESS) { exit_code = EXIT_FAILURE; } term_title_update(NULL); vifm_exit(exit_code); }
char * prepare_targets(FileView *view) { if(view->selected_files > 0) { return expand_macros("%f", NULL, NULL, 1); } if(!flist_custom_active(view)) { return strdup("."); } return (vifm_chdir(flist_get_dir(view)) == 0) ? strdup(".") : NULL; }
/* Composes command to be run using terminal multiplexer. Returns newly * allocated string that should be freed by the caller. */ static char * gen_term_multiplexer_cmd(const char cmd[], int pause) { char *title_arg; char *raw_shell_cmd; char *escaped_shell_cmd; char *shell_cmd = NULL; if(curr_stats.term_multiplexer != TM_TMUX && curr_stats.term_multiplexer != TM_SCREEN) { assert(0 && "Unexpected active terminal multiplexer value."); return NULL; } title_arg = gen_term_multiplexer_title_arg(cmd); raw_shell_cmd = format_str("%s%s", cmd, pause ? PAUSE_STR : ""); escaped_shell_cmd = shell_like_escape(raw_shell_cmd, 0); if(curr_stats.term_multiplexer == TM_TMUX) { char *const arg = format_str("%s -c %s", cfg.shell, escaped_shell_cmd); char *const escaped_arg = shell_like_escape(arg, 0); shell_cmd = format_str("tmux new-window %s %s", title_arg, escaped_arg); free(escaped_arg); free(arg); } else if(curr_stats.term_multiplexer == TM_SCREEN) { set_pwd_in_screen(flist_get_dir(curr_view)); shell_cmd = format_str("screen %s %s -c %s", title_arg, cfg.shell, escaped_shell_cmd); } else { assert(0 && "Unsupported terminal multiplexer type."); } free(escaped_shell_cmd); free(raw_shell_cmd); free(title_arg); return shell_cmd; }
static char * expand_directory_path(FileView *view, char *expanded, int quotes, const char *mod, int for_shell) { const char *const modified = apply_mods(flist_get_dir(view), "/", mod, for_shell); char *const result = append_path_to_expanded(expanded, quotes, modified); #ifdef _WIN32 if(for_shell && curr_stats.shell_type == ST_CMD) { to_back_slash(result); } #endif return result; }
/* Clones one view into another. Path specifies location of active pane and can * be NULL. The destination view is assumed to not own any resources. */ static void clone_view(view_t *dst, view_t *src, const char path[]) { strcpy(dst->curr_dir, path == NULL ? flist_get_dir(src) : path); dst->timestamps_mutex = src->timestamps_mutex; dst->win = src->win; dst->title = src->title; flist_init_view(dst); dst->dir_entry[0].origin = src->curr_dir; clone_local_options(src, dst, 1); matcher_free(dst->manual_filter); dst->manual_filter = matcher_clone(src->manual_filter); filter_assign(&dst->auto_filter, &src->auto_filter); dst->prev_invert = src->prev_invert; dst->invert = src->invert; /* Clone current entry even though we populate file list later to give * reloading reference point for cursor. */ replace_dir_entries(dst, &dst->dir_entry, &dst->list_rows, get_current_entry(src), 1); dst->list_pos = 0; /* Clone viewport configuration. */ dst->curr_line = src->curr_line; dst->top_line = src->top_line; dst->window_rows = src->window_rows; dst->window_cols = src->window_cols; dst->window_cells = src->window_cells; flist_hist_resize(dst, cfg.history_len); flist_hist_clone(dst, src); if(path != NULL && !flist_custom_active(src)) { /* Record location we're leaving. */ flist_hist_save(dst, src->curr_dir, get_current_file_name(src), src->list_pos - src->top_line); } (void)populate_dir_list(dst, path == NULL); /* XXX: do we need to update origins or is this a leftover from before list * population was introduced? */ flist_update_origins(dst, &dst->curr_dir[0], &src->curr_dir[0]); /* Record new location. */ flist_hist_save(dst, NULL, NULL, -1); }
/* Switches to global tab specified by its index if the index is valid. */ static void tabs_goto_global(int idx) { if(current_tab == idx) { return; } //add by sim1 +++++++++++++++++++++++++++++ if(idx < 0) { idx = last_tab; } //add by sim1 ----------------------------- if(idx < 0 || idx >= (int)DA_SIZE(gtabs)) { return; } gtabs[current_tab].left.tabs[gtabs[current_tab].left.current].view = lwin; gtabs[current_tab].right.tabs[gtabs[current_tab].right.current].view = rwin; capture_global_state(>abs[current_tab]); assign_preview(>abs[current_tab].preview, &curr_stats.preview); lwin = gtabs[idx].left.tabs[gtabs[idx].left.current].view; rwin = gtabs[idx].right.tabs[gtabs[idx].right.current].view; if(gtabs[idx].active_pane != (curr_view == &rwin)) { swap_view_roles(); } curr_stats.number_of_windows = (gtabs[idx].only_mode ? 1 : 2); curr_stats.split = gtabs[idx].split; curr_stats.splitter_pos = gtabs[idx].splitter_pos; assign_preview(&curr_stats.preview, >abs[idx].preview); last_tab = current_tab; //add by sim1 current_tab = idx; stats_set_quickview(curr_stats.preview.on); ui_view_schedule_redraw(&lwin); ui_view_schedule_redraw(&rwin); load_view_options(curr_view); (void)vifm_chdir(flist_get_dir(curr_view)); }
/* Reloads all views in all tabs on one side (left/top or right/bottom). */ static void reload_views(view_t *side) { int i; tab_info_t tab_info; for(i = 0; enum_tabs(side, i, &tab_info); ++i) { if(tab_info.view != side) { char *path = strdup(flist_get_dir(tab_info.view)); flist_free_view(tab_info.view); memset(tab_info.view, 0, sizeof(*tab_info.view)); clone_view(tab_info.view, side, path); free(path); } } }
/* Switches to pane tab specified by its index if the index is valid. */ static void tabs_goto_pane(int idx) { pane_tabs_t *const ptabs = get_pane_tabs(curr_view); if(ptabs->current == idx) { return; } //add by sim1 +++++++++++++++++++++++++++++++++++ if(idx < 0) { idx = ptabs->last; if (idx == ptabs->current) { return; } } //add by sim1 ----------------------------------- if(idx < 0 || idx >= (int)DA_SIZE(ptabs->tabs)) { return; } ptabs->tabs[ptabs->current].view = *curr_view; assign_preview(&ptabs->tabs[ptabs->current].preview, &curr_stats.preview); *curr_view = ptabs->tabs[idx].view; assign_preview(&curr_stats.preview, &ptabs->tabs[idx].preview); ptabs->last = ptabs->current; //add by sim1 ptabs->current = idx; stats_set_quickview(curr_stats.preview.on); ui_view_schedule_redraw(curr_view); load_view_options(curr_view); (void)vifm_chdir(flist_get_dir(curr_view)); //add by sim1 for test //ui_sb_msgf("tabs_goto_pane: curr=%d, last=%d, tabs=%d", ptabs->current, ptabs->last, (int)DA_SIZE(ptabs->tabs)); }
/* Runs the cmd in a split window of terminal multiplexer. Runs shell, if cmd * is NULL. */ static void run_in_split(const FileView *view, const char cmd[]) { char *const escaped_cmd = (cmd == NULL) ? strdup(cfg.shell) : shell_like_escape(cmd, 0); setup_shellout_env(); if(curr_stats.term_multiplexer == TM_TMUX) { char cmd[1024]; snprintf(cmd, sizeof(cmd), "tmux split-window %s", escaped_cmd); (void)vifm_system(cmd); } else if(curr_stats.term_multiplexer == TM_SCREEN) { char cmd[1024]; /* "eval" executes each argument as a separate argument, but escaping rules * are not exactly like in shell, so last command is run separately. */ char *const escaped_dir = shell_like_escape(flist_get_dir(view), 0); snprintf(cmd, sizeof(cmd), "screen -X eval chdir\\ %s 'focus bottom' " "split 'focus bottom'", escaped_dir); free(escaped_dir); (void)vifm_system(cmd); snprintf(cmd, sizeof(cmd), "screen -X screen vifm-screen-split %s", escaped_cmd); (void)vifm_system(cmd); } else { assert(0 && "Unexpected active terminal multiplexer value."); } cleanup_shellout_env(); free(escaped_cmd); }
void files_chmod(FileView *view, const char *mode, int recurse_dirs) { char undo_msg[COMMAND_GROUP_INFO_LEN]; dir_entry_t *entry; size_t len; snprintf(undo_msg, sizeof(undo_msg), "chmod in %s: ", replace_home_part(flist_get_dir(view))); len = strlen(undo_msg); ui_cancellation_reset(); entry = NULL; while(iter_selection_or_current(view, &entry) && !ui_cancellation_requested()) { if(len >= 2U && undo_msg[len - 2U] != ':') { strncat(undo_msg + len, ", ", sizeof(undo_msg) - len - 1); len += strlen(undo_msg + len); } strncat(undo_msg + len, entry->name, sizeof(undo_msg) - len - 1); len += strlen(undo_msg + len); } cmd_group_begin(undo_msg); entry = NULL; while(iter_selection_or_current(view, &entry) && !ui_cancellation_requested()) { char inv[16]; snprintf(inv, sizeof(inv), "0%o", entry->mode & 0xff); chmod_file_in_list(view, entry_to_pos(view, entry), mode, inv, recurse_dirs); } cmd_group_end(); }
/* Clones one view into another. The destination view is assumed to not own any * resources. */ static void clone_view(view_t *dst, view_t *src) { strcpy(dst->curr_dir, flist_get_dir(src)); dst->timestamps_mutex = src->timestamps_mutex; dst->win = src->win; dst->title = src->title; flist_init_view(dst); dst->dir_entry[0].origin = src->curr_dir; clone_local_options(src, dst, 1); matcher_free(dst->manual_filter); dst->manual_filter = matcher_clone(src->manual_filter); filter_assign(&dst->auto_filter, &src->auto_filter); dst->prev_invert = src->prev_invert; dst->invert = src->invert; /* Clone current entry even though we populate file list later to give * reloading reference point for cursor. */ replace_dir_entries(dst, &dst->dir_entry, &dst->list_rows, get_current_entry(src), 1); dst->list_pos = 0; /* Clone viewport configuration. */ dst->curr_line = src->curr_line; dst->top_line = src->top_line; dst->window_rows = src->window_rows; dst->window_cols = src->window_cols; dst->window_cells = src->window_cells; flist_hist_resize(dst, cfg.history_len); flist_hist_clone(dst, src); (void)populate_dir_list(dst, 1); flist_update_origins(dst, &dst->curr_dir[0], &src->curr_dir[0]); }
/* Changes attributes of files in the view. */ static void files_attrib(FileView *view, DWORD add, DWORD sub, int recurse_dirs) { char undo_msg[COMMAND_GROUP_INFO_LEN]; dir_entry_t *entry; size_t len; snprintf(undo_msg, sizeof(undo_msg), "chmod in %s: ", replace_home_part(flist_get_dir(view))); len = strlen(undo_msg); ui_cancellation_reset(); entry = NULL; while(iter_selection_or_current(view, &entry) && !ui_cancellation_requested()) { if(len >= 2U && undo_msg[len - 2U] != ':') { strncat(undo_msg + len, ", ", sizeof(undo_msg) - len - 1); len += strlen(undo_msg + len); } strncat(undo_msg + len, entry->name, sizeof(undo_msg) - len - 1); len += strlen(undo_msg + len); } cmd_group_begin(undo_msg); entry = NULL; while(iter_selection_or_current(view, &entry) && !ui_cancellation_requested()) { attrib_file_in_list(view, entry_to_pos(view, entry), add, sub, recurse_dirs); } cmd_group_end(); }
/* args and flags parameters can equal NULL. The string returned needs to be * freed in the calling function. After executing flags is one of MF_* * values. */ static char * expand_macros_i(const char command[], const char args[], MacroFlags *flags, int for_shell, macro_filter_func filter) { /* TODO: refactor this function expand_macros() */ /* FIXME: repetitive len = strlen(expanded) could be optimized. */ static const char MACROS_WITH_QUOTING[] = "cCfFbdDr"; size_t cmd_len; char *expanded; size_t x; int len = 0; set_flags(flags, MF_NONE); cmd_len = strlen(command); for(x = 0; x < cmd_len; x++) if(command[x] == '%') break; if(x >= cmd_len) { return strdup(command); } expanded = calloc(cmd_len + 1, sizeof(char)); strncat(expanded, command, x); x++; len = strlen(expanded); do { size_t y; char *p; int quotes = 0; if(command[x] == '"' && char_is_one_of(MACROS_WITH_QUOTING, command[x + 1])) { quotes = 1; ++x; } switch(filter("es, command[x], command[x] == '\0' ? '\0' : command[x + 1])) { case 'a': /* user arguments */ if(args != NULL) { expanded = append_to_expanded(expanded, args); len = strlen(expanded); } break; case 'b': /* selected files of both dirs */ expanded = append_selected_files(curr_view, expanded, 0, quotes, command + x + 1, for_shell); expanded = append_to_expanded(expanded, " "); expanded = append_selected_files(other_view, expanded, 0, quotes, command + x + 1, for_shell); len = strlen(expanded); break; case 'c': /* current dir file under the cursor */ expanded = append_selected_files(curr_view, expanded, 1, quotes, command + x + 1, for_shell); len = strlen(expanded); break; case 'C': /* other dir file under the cursor */ expanded = append_selected_files(other_view, expanded, 1, quotes, command + x + 1, for_shell); len = strlen(expanded); break; case 'f': /* current dir selected files */ expanded = append_selected_files(curr_view, expanded, 0, quotes, command + x + 1, for_shell); len = strlen(expanded); break; case 'F': /* other dir selected files */ expanded = append_selected_files(other_view, expanded, 0, quotes, command + x + 1, for_shell); len = strlen(expanded); break; case 'd': /* current directory */ expanded = expand_directory_path(curr_view, expanded, quotes, command + x + 1, for_shell); len = strlen(expanded); break; case 'D': /* Directory of the other view. */ expanded = expand_directory_path(other_view, expanded, quotes, command + x + 1, for_shell); len = strlen(expanded); break; case 'n': /* Forbid using of terminal multiplexer, even if active. */ set_flags(flags, MF_NO_TERM_MUX); break; case 'm': /* Use menu. */ set_flags(flags, MF_MENU_OUTPUT); break; case 'M': /* Use menu like with :locate and :find. */ set_flags(flags, MF_MENU_NAV_OUTPUT); break; case 'S': /* Show command output in the status bar */ set_flags(flags, MF_STATUSBAR_OUTPUT); break; case 's': /* Split in new screen region and execute command there. */ set_flags(flags, MF_SPLIT); break; case 'u': /* Parse output as list of files and compose custom view. */ set_flags(flags, MF_CUSTOMVIEW_OUTPUT); break; case 'U': /* Parse output as list of files and compose unsorted view. */ set_flags(flags, MF_VERYCUSTOMVIEW_OUTPUT); break; case 'i': /* Ignore output. */ set_flags(flags, MF_IGNORE); break; case 'r': /* Registers' content. */ { int well_formed; expanded = expand_register(flist_get_dir(curr_view), expanded, quotes, command + x + 2, command[x + 1], &well_formed, for_shell); len = strlen(expanded); if(well_formed) { x++; } } break; case 'p': /* Preview pane properties. */ { int well_formed; const char key = command[x + 1]; if(key == 'c') { return expanded; } expanded = expand_preview(expanded, key, &well_formed); len = strlen(expanded); if(well_formed) { ++x; } } break; case '%': expanded = append_to_expanded(expanded, "%"); len = strlen(expanded); break; case '\0': if(char_is_one_of("pr", command[x]) && command[x + 1] != '\0') { ++x; } break; } if(command[x] != '\0') x++; while(x < cmd_len) { size_t len = get_mods_len(command + x); if(len == 0) { break; } x += len; } y = x; while(x < cmd_len) { if(command[x] == '%') break; if(command[x] != '\0') x++; } assert(x >= y); assert(y <= cmd_len); p = realloc(expanded, len + (x - y) + 1); if(p == NULL) { free(expanded); return NULL; } expanded = p; strncat(expanded, command + y, x - y); len = strlen(expanded); ++x; } while(x < cmd_len); return expanded; }
/* Entry-point. Has same semantics as main(). */ static int vifm_main(int argc, char *argv[]) { /* TODO: refactor vifm_main() function */ static const int quit = 0; char **files = NULL; int nfiles = 0; int lwin_cv, rwin_cv; char dir[PATH_MAX + 1]; if(get_start_cwd(dir, sizeof(dir)) != 0) { return -1; } args_parse(&vifm_args, argc, argv, dir); args_process(&vifm_args, 1); lwin_cv = (strcmp(vifm_args.lwin_path, "-") == 0 && vifm_args.lwin_handle); rwin_cv = (strcmp(vifm_args.rwin_path, "-") == 0 && vifm_args.rwin_handle); if(lwin_cv || rwin_cv) { files = read_stream_lines(stdin, &nfiles, 1, NULL, NULL); if(reopen_term_stdin() != 0) { free_string_array(files, nfiles); return EXIT_FAILURE; } } (void)setlocale(LC_ALL, ""); srand(time(NULL)); cfg_init(); if(vifm_args.logging) { init_logger(1, vifm_args.startup_log_path); } init_filelists(); tabs_init(); regs_init(); cfg_discover_paths(); reinit_logger(cfg.log_file); /* Commands module also initializes bracket notation and variables. */ init_commands(); init_builtin_functions(); update_path_env(1); if(stats_init(&cfg) != 0) { free_string_array(files, nfiles); puts("Error during session status initialization."); return -1; } /* Tell file type module what function to use to check availability of * external programs. */ ft_init(&external_command_exists); /* This should be called before loading any configuration file. */ ft_reset(curr_stats.exec_env_type == EET_EMULATOR_WITH_X); init_option_handlers(); if(!vifm_args.no_configs) { /* vifminfo must be processed this early so that it can restore last visited * directory. */ read_info_file(0); } curr_stats.ipc = ipc_init(vifm_args.server_name, &parse_received_arguments, &eval_received_expression); if(ipc_enabled() && curr_stats.ipc == NULL) { fputs("Failed to initialize IPC unit", stderr); return -1; } /* Export chosen server name to parsing unit. */ { var_t var = var_from_str(ipc_get_name(curr_stats.ipc)); setvar("v:servername", var); var_free(var); } args_process(&vifm_args, 0); bg_init(); fops_init(&enter_prompt_mode, &prompt_msg_custom); set_view_path(&lwin, vifm_args.lwin_path); set_view_path(&rwin, vifm_args.rwin_path); if(need_to_switch_active_pane(vifm_args.lwin_path, vifm_args.rwin_path)) { swap_view_roles(); } load_initial_directory(&lwin, dir); load_initial_directory(&rwin, dir); /* Force split view when two paths are specified on command-line. */ if(vifm_args.lwin_path[0] != '\0' && vifm_args.rwin_path[0] != '\0') { curr_stats.number_of_windows = 2; } /* Prepare terminal for further operations. */ curr_stats.original_stdout = reopen_term_stdout(); if(curr_stats.original_stdout == NULL) { free_string_array(files, nfiles); return -1; } if(!setup_ncurses_interface()) { free_string_array(files, nfiles); return -1; } init_modes(); un_init(&undo_perform_func, NULL, &ui_cancellation_requested, &cfg.undo_levels); load_view_options(curr_view); curr_stats.load_stage = 1; /* Make v:count exist during processing configuration. */ set_count_vars(0); if(!vifm_args.no_configs) { load_scheme(); cfg_load(); } if(lwin_cv || rwin_cv) { flist_custom_set(lwin_cv ? &lwin : &rwin, "-", dir, files, nfiles); } free_string_array(files, nfiles); cs_load_pairs(); cs_write(); setup_signals(); /* Ensure trash directories exist, it might not have been called during * configuration file sourcing if there is no `set trashdir=...` command. */ (void)set_trash_dir(cfg.trash_dir); check_path_for_file(&lwin, vifm_args.lwin_path, vifm_args.lwin_handle); check_path_for_file(&rwin, vifm_args.rwin_path, vifm_args.rwin_handle); curr_stats.load_stage = 2; /* Update histories of the views to ensure that their current directories, * which might have been set using command-line parameters, are stored in the * history. This is not done automatically as history manipulation should be * postponed until views are fully loaded, otherwise there is no correct * information about current file and relative cursor position. */ flist_hist_save(&lwin, NULL, NULL, -1); flist_hist_save(&rwin, NULL, NULL, -1); /* Trigger auto-commands for initial directories. */ if(!lwin_cv) { (void)vifm_chdir(flist_get_dir(&lwin)); vle_aucmd_execute("DirEnter", flist_get_dir(&lwin), &lwin); } if(!rwin_cv) { (void)vifm_chdir(flist_get_dir(&rwin)); vle_aucmd_execute("DirEnter", flist_get_dir(&rwin), &rwin); } update_screen(UT_FULL); modes_update(); /* Run startup commands after loading file lists into views, so that commands * like +1 work. */ exec_startup_commands(&vifm_args); curr_stats.load_stage = 3; event_loop(&quit); return 0; }
void vifm_restart(void) { view_t *tmp_view; curr_stats.restart_in_progress = 1; /* All user mappings in all modes. */ vle_keys_user_clear(); /* User defined commands. */ vle_cmds_run("comclear"); /* Autocommands. */ vle_aucmd_remove(NULL, NULL); /* All kinds of histories. */ cfg_resize_histories(0); /* Session status. Must be reset _before_ options, because options take some * of values from status. */ (void)stats_reset(&cfg); /* Options of current pane. */ vle_opts_restore_defaults(); /* Options of other pane. */ tmp_view = curr_view; curr_view = other_view; load_view_options(other_view); vle_opts_restore_defaults(); curr_view = tmp_view; /* File types and viewers. */ ft_reset(curr_stats.exec_env_type == EET_EMULATOR_WITH_X); /* Undo list. */ un_reset(); /* Directory stack. */ dir_stack_clear(); /* Registers. */ regs_reset(); /* Clear all marks and bookmarks. */ clear_all_marks(); bmarks_clear(); /* Reset variables. */ clear_envvars(); init_variables(); /* This update is needed as clear_variables() will reset $PATH. */ update_path_env(1); reset_views(); read_info_file(1); flist_hist_save(&lwin, NULL, NULL, -1); flist_hist_save(&rwin, NULL, NULL, -1); /* Color schemes. */ if(stroscmp(curr_stats.color_scheme, DEF_CS_NAME) != 0 && cs_exists(curr_stats.color_scheme)) { cs_load_primary(curr_stats.color_scheme); } else { cs_load_defaults(); } cs_load_pairs(); cfg_load(); /* Reloading of tabs needs to happen after configuration is read so that new * values from lwin and rwin got propagated. */ tabs_reload(); exec_startup_commands(&vifm_args); curr_stats.restart_in_progress = 0; /* Trigger auto-commands for initial directories. */ (void)vifm_chdir(flist_get_dir(&lwin)); vle_aucmd_execute("DirEnter", flist_get_dir(&lwin), &lwin); (void)vifm_chdir(flist_get_dir(&rwin)); vle_aucmd_execute("DirEnter", flist_get_dir(&rwin), &rwin); update_screen(UT_REDRAW); }
int fuse_try_unmount(FileView *view) { char buf[14 + PATH_MAX + 1]; fuse_mount_t *runner, *trailer; int status; fuse_mount_t *sniffer; char *escaped_mount_point; runner = fuse_mounts; trailer = NULL; while(runner) { if(paths_are_equal(runner->mount_point, view->curr_dir)) { break; } trailer = runner; runner = runner->next; } if(runner == NULL) { return 0; } /* we are exiting a top level dir */ escaped_mount_point = escape_filename(runner->mount_point, 0); snprintf(buf, sizeof(buf), "%s %s 2> /dev/null", curr_stats.fuse_umount_cmd, escaped_mount_point); LOG_INFO_MSG("FUSE unmount command: `%s`", buf); free(escaped_mount_point); /* Have to chdir to parent temporarily, so that this DIR can be unmounted. */ if(vifm_chdir(cfg.fuse_home) != 0) { show_error_msg("FUSE UMOUNT ERROR", "Can't chdir to FUSE home"); return -1; } status_bar_message("FUSE unmounting selected file, please stand by.."); status = background_and_wait_for_status(buf, 0, NULL); clean_status_bar(); /* check child status */ if(!WIFEXITED(status) || WEXITSTATUS(status)) { werase(status_bar); show_error_msgf("FUSE UMOUNT ERROR", "Can't unmount %s. It may be busy.", runner->source_file_name); (void)vifm_chdir(flist_get_dir(view)); return -1; } /* remove the directory we created for the mount */ if(path_exists(runner->mount_point, DEREF)) rmdir(runner->mount_point); /* remove mount point from fuse_mount_t */ sniffer = runner->next; if(trailer) trailer->next = sniffer ? sniffer : NULL; else fuse_mounts = sniffer; updir_from_mount(view, runner); free(runner); return 1; }
/* mount_point should be an array of at least PATH_MAX characters * Returns non-zero on error. */ static int fuse_mount(FileView *view, char file_full_path[], const char param[], const char program[], char mount_point[]) { /* TODO: refactor this function fuse_mount(). */ int mount_point_id; char buf[2*PATH_MAX]; char *escaped_filename; int foreground; char errors_file[PATH_MAX]; int status; int cancelled; escaped_filename = escape_filename(get_current_file_name(view), 0); mount_point_id = get_last_mount_point_id(fuse_mounts); do { snprintf(mount_point, PATH_MAX, "%s/%03d_%s", cfg.fuse_home, ++mount_point_id, get_current_file_name(view)); } while(path_exists(mount_point, DEREF)); if(os_mkdir(mount_point, S_IRWXU) != 0) { free(escaped_filename); show_error_msg("Unable to create FUSE mount directory", mount_point); return -1; } free(escaped_filename); /* Just before running the mount, I need to chdir out temporarily from any FUSE mounted paths, Otherwise the fuse-zip command fails with "fusermount: failed to open current directory: permission denied" (this happens when mounting JARs from mounted JARs) */ if(vifm_chdir(cfg.fuse_home) != 0) { show_error_msg("FUSE MOUNT ERROR", "Can't chdir() to FUSE home"); return -1; } format_mount_command(mount_point, file_full_path, param, program, sizeof(buf), buf, &foreground); status_bar_message("FUSE mounting selected file, please stand by.."); if(foreground) { def_prog_mode(); endwin(); } generate_tmp_file_name("vifm.errors", errors_file, sizeof(errors_file)); strcat(buf, " 2> "); strcat(buf, errors_file); LOG_INFO_MSG("FUSE mount command: `%s`", buf); status = background_and_wait_for_status(buf, !foreground, &cancelled); clean_status_bar(); /* Check child process exit status. */ if(!WIFEXITED(status) || WEXITSTATUS(status) != EXIT_SUCCESS) { FILE *ef; if(!WIFEXITED(status)) { LOG_ERROR_MSG("FUSE mounter didn't exit!"); } else { LOG_ERROR_MSG("FUSE mount command exit status: %d", WEXITSTATUS(status)); } ef = os_fopen(errors_file, "r"); if(ef == NULL) { LOG_SERROR_MSG(errno, "Failed to open temporary stderr file: %s", errors_file); } show_errors_from_file(ef, "FUSE mounter error"); werase(status_bar); if(cancelled) { status_bar_message("FUSE mount cancelled"); curr_stats.save_msg = 1; } else { show_error_msg("FUSE MOUNT ERROR", file_full_path); } if(unlink(errors_file) != 0) { LOG_SERROR_MSG(errno, "Error file deletion failure: %d", errors_file); } /* Remove the directory we created for the mount. */ (void)rmdir(mount_point); (void)vifm_chdir(flist_get_dir(view)); return -1; } unlink(errors_file); status_bar_message("FUSE mount success"); register_mount(&fuse_mounts, file_full_path, mount_point, mount_point_id); return 0; }
void event_loop(const int *quit) { /* TODO: refactor this function event_loop(). */ LOG_FUNC_ENTER; const wchar_t *const prev_input_buf = curr_input_buf; const size_t *const prev_input_buf_pos = curr_input_buf_pos; wchar_t input_buf[128]; size_t input_buf_pos; int last_result = 0; int wait_for_enter = 0; int wait_for_suggestion = 0; int timeout = cfg.timeout_len; input_buf[0] = L'\0'; input_buf_pos = 0; curr_input_buf = &input_buf[0]; curr_input_buf_pos = &input_buf_pos; /* Make sure to set the working directory once in order to have the * desired state even before any events are processed. */ (void)vifm_chdir(flist_get_dir(curr_view)); while(!*quit) { wint_t c; size_t counter; int got_input; lwin.user_selection = 1; rwin.user_selection = 1; modes_pre(); /* Waits for timeout then skips if no key press. Short-circuit if we're not * waiting for the next key after timeout. */ do { const int actual_timeout = wait_for_suggestion ? MIN(timeout, cfg.sug.delay) : timeout; if(!ensure_term_is_ready()) { wait_for_enter = 0; continue; } modes_periodic(); bg_check(); got_input = (get_char_async_loop(status_bar, &c, actual_timeout) != ERR); /* If suggestion delay timed out, reset it and wait the rest of the * timeout. */ if(!got_input && wait_for_suggestion) { wait_for_suggestion = 0; timeout -= actual_timeout; display_suggestion_box(input_buf); continue; } wait_for_suggestion = 0; if(!got_input && (input_buf_pos == 0 || last_result == KEYS_WAIT)) { timeout = cfg.timeout_len; continue; } if(got_input && c == K(KEY_RESIZE)) { modes_redraw(); continue; } break; } while(1); suggestions_are_visible = 0; /* Ensure that current working directory is set correctly (some pieces of * code rely on this, e.g. %c macro in current directory). */ (void)vifm_chdir(flist_get_dir(curr_view)); if(got_input) { if(wait_for_enter) { wait_for_enter = 0; curr_stats.save_msg = 0; ui_sb_clear(); if(c == WC_CR) { continue; } } if(c == WC_C_z) { ui_shutdown(); stop_process(); continue; } if(input_buf_pos < ARRAY_LEN(input_buf) - 2) { input_buf[input_buf_pos++] = c; input_buf[input_buf_pos] = L'\0'; } else { /* Recover from input buffer overflow by resetting its contents. */ reset_input_buf(input_buf, &input_buf_pos); clear_input_bar(); continue; } } counter = vle_keys_counter(); if(!got_input && last_result == KEYS_WAIT_SHORT) { hide_suggestion_box(); last_result = vle_keys_exec_timed_out(input_buf); counter = vle_keys_counter() - counter; assert(counter <= input_buf_pos); if(counter > 0) { memmove(input_buf, input_buf + counter, (wcslen(input_buf) - counter + 1)*sizeof(wchar_t)); } } else { if(got_input) { curr_stats.save_msg = 0; } if(last_result == KEYS_WAIT || last_result == KEYS_WAIT_SHORT) { hide_suggestion_box(); } last_result = vle_keys_exec(input_buf); counter = vle_keys_counter() - counter; assert(counter <= input_buf_pos); if(counter > 0) { input_buf_pos -= counter; memmove(input_buf, input_buf + counter, (wcslen(input_buf) - counter + 1)*sizeof(wchar_t)); } if(last_result == KEYS_WAIT || last_result == KEYS_WAIT_SHORT) { if(should_display_suggestion_box()) { wait_for_suggestion = 1; } if(got_input) { modupd_input_bar(input_buf); } if(last_result == KEYS_WAIT_SHORT && wcscmp(input_buf, L"\033") == 0) { timeout = 1; } if(counter > 0) { clear_input_bar(); } if(!curr_stats.save_msg && curr_view->selected_files && !vle_mode_is(CMDLINE_MODE)) { print_selected_msg(); } continue; } } timeout = cfg.timeout_len; process_scheduled_updates(); reset_input_buf(input_buf, &input_buf_pos); clear_input_bar(); if(ui_sb_multiline()) { wait_for_enter = 1; update_all_windows(); continue; } /* Ensure that current working directory is set correctly (some pieces of * code rely on this). PWD could be changed during command execution, but * it should be correct for modes_post() in case of preview modes. */ (void)vifm_chdir(flist_get_dir(curr_view)); modes_post(); } curr_input_buf = prev_input_buf; curr_input_buf_pos = prev_input_buf_pos; }
/* Makes sorted by path list of entries that. The trie is used to keep track of * identical files. With non-zero dups_only, new files aren't added to the * trie. */ static entries_t make_diff_list(trie_t *trie, FileView *view, int *next_id, CompareType ct, int skip_empty, int dups_only) { int i; strlist_t files = {}; entries_t r = {}; int last_progress = 0; show_progress("Listing...", 0); if(flist_custom_active(view) && ONE_OF(view->custom.type, CV_REGULAR, CV_VERY)) { list_view_entries(view, &files); } else { list_files_recursively(flist_get_dir(view), view->hide_dot, &files); } show_progress("Querying...", 0); for(i = 0; i < files.nitems && !ui_cancellation_requested(); ++i) { char progress_msg[128]; int progress; int existing_id; char *fingerprint; const char *const path = files.items[i]; dir_entry_t *const entry = entry_list_add(view, &r.entries, &r.nentries, path); if(skip_empty && entry->size == 0) { free_dir_entry(view, entry); --r.nentries; continue; } fingerprint = get_file_fingerprint(path, entry, ct); /* In case we couldn't obtain fingerprint (e.g., comparing by contents and * files isn't readable), ignore the file and keep going. */ if(is_null_or_empty(fingerprint)) { free(fingerprint); free_dir_entry(view, entry); --r.nentries; continue; } entry->tag = i; if(get_file_id(trie, path, fingerprint, &existing_id, ct)) { entry->id = existing_id; } else if(dups_only) { entry->id = -1; } else { entry->id = *next_id; ++*next_id; put_file_id(trie, path, fingerprint, entry->id, ct); } free(fingerprint); progress = (i*100)/files.nitems; if(progress != last_progress) { last_progress = progress; snprintf(progress_msg, sizeof(progress_msg), "Querying... %d (% 2d%%)", i, progress); show_progress(progress_msg, -1); } } free_string_array(files.items, files.nitems); return r; }
void event_loop(const int *quit) { /* TODO: refactor this function event_loop(). */ LOG_FUNC_ENTER; const wchar_t *const prev_input_buf = curr_input_buf; const size_t *const prev_input_buf_pos = curr_input_buf_pos; wchar_t input_buf[128]; size_t input_buf_pos; int last_result = 0; int wait_for_enter = 0; int timeout = cfg.timeout_len; input_buf[0] = L'\0'; input_buf_pos = 0; curr_input_buf = &input_buf[0]; curr_input_buf_pos = &input_buf_pos; while(!*quit) { wint_t c; size_t counter; int got_input; if(!ensure_term_is_ready()) { wait_for_enter = 0; continue; } lwin.user_selection = 1; rwin.user_selection = 1; modes_pre(); /* Waits for timeout then skips if no keypress. Short-circuit if we're not * waiting for the next key after timeout. */ do { modes_periodic(); check_background_jobs(); got_input = get_char_async_loop(status_bar, &c, timeout) != ERR; if(!got_input && input_buf_pos == 0) { timeout = cfg.timeout_len; continue; } break; } while(1); /* Ensure that current working directory is set correctly (some pieces of * code rely on this). */ (void)vifm_chdir(flist_get_dir(curr_view)); if(got_input) { if(wait_for_enter) { wait_for_enter = 0; curr_stats.save_msg = 0; clean_status_bar(); if(c == L'\x0d') { continue; } } if(c == L'\x1a') /* Ctrl-Z */ { def_prog_mode(); endwin(); stop_process(); continue; } if(input_buf_pos < ARRAY_LEN(input_buf) - 2) { input_buf[input_buf_pos++] = c; input_buf[input_buf_pos] = L'\0'; } else { /* Recover from input buffer overflow by resetting its contents. */ reset_input_buf(input_buf, &input_buf_pos); clear_input_bar(); continue; } } counter = get_key_counter(); if(!got_input && last_result == KEYS_WAIT_SHORT) { last_result = execute_keys_timed_out(input_buf); counter = get_key_counter() - counter; assert(counter <= input_buf_pos); if(counter > 0) { memmove(input_buf, input_buf + counter, (wcslen(input_buf) - counter + 1)*sizeof(wchar_t)); } } else { if(got_input) { curr_stats.save_msg = 0; } last_result = execute_keys(input_buf); counter = get_key_counter() - counter; assert(counter <= input_buf_pos); if(counter > 0) { input_buf_pos -= counter; memmove(input_buf, input_buf + counter, (wcslen(input_buf) - counter + 1)*sizeof(wchar_t)); } if(last_result == KEYS_WAIT || last_result == KEYS_WAIT_SHORT) { if(got_input) { modupd_input_bar(input_buf); } if(last_result == KEYS_WAIT_SHORT && wcscmp(input_buf, L"\033") == 0) { timeout = 1; } if(counter > 0) { clear_input_bar(); } if(!curr_stats.save_msg && curr_view->selected_files && !vle_mode_is(CMDLINE_MODE)) { print_selected_msg(); } continue; } } timeout = cfg.timeout_len; process_scheduled_updates(); reset_input_buf(input_buf, &input_buf_pos); clear_input_bar(); if(is_status_bar_multiline()) { wait_for_enter = 1; update_all_windows(); continue; } /* Ensure that current working directory is set correctly (some pieces of * code rely on this). PWD could be changed during command execution, but * it should be correct for modes_post() in case of preview modes. */ (void)vifm_chdir(flist_get_dir(curr_view)); modes_post(); } curr_input_buf = prev_input_buf; curr_input_buf_pos = prev_input_buf_pos; }
/* Checks whether menu working directory and current directory of the view are * in sync. Returns non-zero if so, otherwise zero is returned. */ static int menu_and_view_are_in_sync(const menu_data_t *m, const FileView *view) { /* NULL check is for tests. */ return (view == NULL || paths_are_same(m->cwd, flist_get_dir(view))); }
int compare_move(FileView *from, FileView *to) { char from_path[PATH_MAX], to_path[PATH_MAX]; char *from_fingerprint, *to_fingerprint; const CompareType ct = from->custom.diff_cmp_type; dir_entry_t *const curr = &from->dir_entry[from->list_pos]; dir_entry_t *const other = &to->dir_entry[from->list_pos]; if(from->custom.type != CV_DIFF || !from->custom.diff_path_group) { status_bar_error("Not in diff mode with path grouping"); return 1; } if(curr->id == other->id && !fentry_is_fake(curr) && !fentry_is_fake(other)) { /* Nothing to do if files are already equal. */ return 0; } /* We're going at least to try to update one of views (which might refer to * the same directory), so schedule a reload. */ ui_view_schedule_reload(from); ui_view_schedule_reload(to); if(fentry_is_fake(curr)) { /* Just remove the other file (it can't be fake entry too). */ return fops_delete_current(to, 1, 0); } get_full_path_of(curr, sizeof(from_path), from_path); get_full_path_of(other, sizeof(to_path), to_path); if(fentry_is_fake(other)) { char to_path[PATH_MAX]; char canonical[PATH_MAX]; snprintf(to_path, sizeof(to_path), "%s/%s/%s", flist_get_dir(to), curr->origin + strlen(flist_get_dir(from)), curr->name); canonicalize_path(to_path, canonical, sizeof(canonical)); /* Copy current file to position of the other one using relative path with * different base. */ fops_replace(from, canonical, 0); /* Update the other entry to not be fake. */ remove_last_path_component(canonical); replace_string(&other->name, curr->name); replace_string(&other->origin, canonical); } else { /* Overwrite file in the other pane with corresponding file from current * pane. */ fops_replace(from, to_path, 1); } /* Obtaining file fingerprint relies on size field of entries, so try to load * it and ignore if it fails. */ other->size = get_file_size(to_path); /* Try to update id of the other entry by computing fingerprint of both files * and checking if they match. */ from_fingerprint = get_file_fingerprint(from_path, curr, ct); to_fingerprint = get_file_fingerprint(to_path, other, ct); if(!is_null_or_empty(from_fingerprint) && !is_null_or_empty(to_fingerprint)) { int match = (strcmp(from_fingerprint, to_fingerprint) == 0); if(match && ct == CT_CONTENTS) { match = files_are_identical(from_path, to_path); } if(match) { other->id = curr->id; } } free(from_fingerprint); free(to_fingerprint); return 0; }
/* Returns negative value in case of error */ static int execute_command(FileView *view, const char command[], int menu) { int id; int result; if(command == NULL) { flist_sel_stash_if_nonempty(view); return 0; } command = skip_to_cmd_name(command); if(command[0] == '"') return 0; if(command[0] == '\0' && !menu) { flist_sel_stash_if_nonempty(view); return 0; } if(!menu) { init_cmds(1, &cmds_conf); cmds_conf.begin = 0; cmds_conf.current = view->list_pos; cmds_conf.end = view->list_rows - 1; } id = get_cmd_id(command); if(!cmd_should_be_processed(id)) { return 0; } if(id == USER_CMD_ID) { char undo_msg[COMMAND_GROUP_INFO_LEN]; snprintf(undo_msg, sizeof(undo_msg), "in %s: %s", replace_home_part(flist_get_dir(view)), command); cmd_group_begin(undo_msg); cmd_group_end(); } keep_view_selection = 0; result = execute_cmd(command); if(result >= 0) return result; switch(result) { case CMDS_ERR_LOOP: status_bar_error("Loop in commands"); break; case CMDS_ERR_NO_MEM: status_bar_error("Unable to allocate enough memory"); break; case CMDS_ERR_TOO_FEW_ARGS: status_bar_error("Too few arguments"); break; case CMDS_ERR_TRAILING_CHARS: status_bar_error("Trailing characters"); break; case CMDS_ERR_INCORRECT_NAME: status_bar_error("Incorrect command name"); break; case CMDS_ERR_NEED_BANG: status_bar_error("Add bang to force"); break; case CMDS_ERR_NO_BUILTIN_REDEFINE: status_bar_error("Can't redefine builtin command"); break; case CMDS_ERR_INVALID_CMD: status_bar_error("Invalid command name"); break; case CMDS_ERR_NO_BANG_ALLOWED: status_bar_error("No ! is allowed"); break; case CMDS_ERR_NO_RANGE_ALLOWED: status_bar_error("No range is allowed"); break; case CMDS_ERR_NO_QMARK_ALLOWED: status_bar_error("No ? is allowed"); break; case CMDS_ERR_INVALID_RANGE: /* message dialog is enough */ break; case CMDS_ERR_NO_SUCH_UDF: status_bar_error("No such user defined command"); break; case CMDS_ERR_UDF_IS_AMBIGUOUS: status_bar_error("Ambiguous use of user-defined command"); break; case CMDS_ERR_ZERO_COUNT: status_bar_error("Zero count"); break; case CMDS_ERR_INVALID_ARG: status_bar_error("Invalid argument"); break; case CMDS_ERR_CUSTOM: /* error message is posted by command handler */ break; default: status_bar_error("Unknown error"); break; } if(!menu && vle_mode_is(NORMAL_MODE)) { flist_sel_stash_if_nonempty(view); } return -1; }