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; }
void sort_entries(view_t *v, entries_t entries) { if(v->sort_g[0] > SK_LAST) { /* Completely skip sorting if primary key isn't set. */ return; } view = v; view_sort = v->sort_g; view_sort_groups = v->sort_groups_g; custom_view = flist_custom_active(v); sort_sequence(entries.entries, entries.nentries); }
/* 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); }
/* Gets string representation of file type. Returns the string. */ static var_t filetype_builtin(const call_info_t *call_info) { char *str_val = var_to_string(call_info->argv[0]); const int fnum = get_fnum(str_val); var_val_t var_val = { .string = "" }; free(str_val); if(fnum >= 0) { const FileType type = curr_view->dir_entry[fnum].type; var_val.const_string = get_type_str(type); } return var_new(VTYPE_STRING, var_val); } /* Returns file type from position or -1 if the position has wrong value. */ static int get_fnum(const char position[]) { if(strcmp(position, ".") == 0) { return curr_view->list_pos; } return -1; } /* Retrieves type of current pane as a string. */ static var_t getpanetype_builtin(const call_info_t *call_info) { FileView *const view = curr_view; var_val_t var_val; if(flist_custom_active(view)) { var_val.string = (view->custom.unsorted ? "very-custom" : "custom"); } else { var_val.string = "regular"; } return var_new(VTYPE_STRING, var_val); }
void follow_file(FileView *view) { if(flist_custom_active(view)) { /* Entry might be freed on navigation, so make sure name and origin will * remain available for the call. */ const dir_entry_t *const entry = &view->dir_entry[view->list_pos]; char *const name = strdup(entry->name); char *const origin = strdup(entry->origin); navigate_to_file(view, origin, name, 0); free(origin); free(name); return; } handle_file(view, FHE_RUN, FHL_FOLLOW); }
TSTATIC char * append_selected_files(FileView *view, char expanded[], int under_cursor, int quotes, const char mod[], int for_shell) { const PathType type = (view == other_view) ? PT_FULL : (flist_custom_active(view) ? PT_REL : PT_NAME); #ifdef _WIN32 size_t old_len = strlen(expanded); #endif if(view->selected_files && !under_cursor) { int n = 0; dir_entry_t *entry = NULL; while(iter_selected_entries(view, &entry)) { expanded = append_entry(view, expanded, type, entry, quotes, mod, for_shell); if(++n != view->selected_files) { expanded = append_to_expanded(expanded, " "); } } } else { expanded = append_entry(view, expanded, type, get_current_entry(view), quotes, mod, for_shell); } #ifdef _WIN32 if(for_shell && curr_stats.shell_type == ST_CMD) { to_back_slash(expanded + old_len); } #endif return expanded; }
void local_filter_apply(view_t *view, const char filter[]) { if(view->local_filter.in_progress) { assert(!view->local_filter.in_progress && "Wrong local filter applying."); return; } (void)filter_set(&view->local_filter.filter, filter); hists_filter_save(view->local_filter.filter.raw); if(flist_custom_active(view) && view->custom.type != CV_TREE && view->local_filter.entry_count == 0) { /* Save unfiltered (by local filter) list for further use so it can be * restored on changing local filter. */ replace_dir_entries(view, &view->local_filter.entries, &view->local_filter.entry_count, view->dir_entry, view->list_rows); } ui_view_schedule_reload(view); }
/* 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 name_filters_add_selection(view_t *view) { dir_entry_t *entry; filter_t filter; int filtered; (void)filter_init(&filter, FILTER_DEF_CASE_SENSITIVITY); /* Traverse items and update/create filter values. */ entry = NULL; while(iter_selection_or_current(view, &entry)) { const char *name = entry->name; char name_with_slash[NAME_MAX + 1 + 1]; if(fentry_is_dir(entry)) { append_slash(entry->name, name_with_slash, sizeof(name_with_slash)); name = name_with_slash; } (void)filter_append(&view->auto_filter, name); (void)filter_append(&filter, name); } /* Even current file might be unavailable for filtering. In this case, just * do nothing. */ if(filter_is_empty(&filter)) { filter_dispose(&filter); return; } if(view->custom.type == CV_DIFF) { (void)filter_in_compare(view, &filter, &is_newly_filtered); ui_view_schedule_redraw(view); filter_dispose(&filter); return; } /* Update entry lists to remove entries that must be filtered out now. No * view reload is needed. */ filtered = zap_entries(view, view->dir_entry, &view->list_rows, &is_newly_filtered, &filter, 0, 1); if(flist_custom_active(view)) { (void)zap_entries(view, view->local_filter.entries, &view->local_filter.entry_count, &is_newly_filtered, &filter, 1, 1); } else { view->filtered += filtered; } filter_dispose(&filter); fpos_ensure_valid_pos(view); ui_view_schedule_redraw(view); }
int flist_find_group(const FileView *view, int next) { /* TODO: refactor/simplify this function (flist_find_group()). */ const int correction = next ? -1 : 0; const int lb = correction; const int ub = view->list_rows + correction; const int inc = next ? +1 : -1; int pos = view->list_pos; dir_entry_t *pentry = &view->dir_entry[pos]; const char *ext = get_last_ext(pentry->name); size_t char_width = utf8_chrw(pentry->name); wchar_t ch = towupper(get_first_wchar(pentry->name)); const SortingKey sorting_key = flist_custom_active(view) && cv_compare(view->custom.type) ? SK_BY_ID : abs(view->sort[0]); const int is_dir = fentry_is_dir(pentry); const char *const type_str = get_type_str(pentry->type); regmatch_t pmatch = { .rm_so = 0, .rm_eo = 0 }; #ifndef _WIN32 char perms[16]; get_perm_string(perms, sizeof(perms), pentry->mode); #endif if(sorting_key == SK_BY_GROUPS) { pmatch = get_group_match(&view->primary_group, pentry->name); } while(pos > lb && pos < ub) { dir_entry_t *nentry; pos += inc; nentry = &view->dir_entry[pos]; switch(sorting_key) { case SK_BY_FILEEXT: if(fentry_is_dir(nentry)) { if(strncmp(pentry->name, nentry->name, char_width) != 0) { return pos; } } if(strcmp(get_last_ext(nentry->name), ext) != 0) { return pos; } break; case SK_BY_EXTENSION: if(strcmp(get_last_ext(nentry->name), ext) != 0) return pos; break; case SK_BY_GROUPS: { regmatch_t nmatch = get_group_match(&view->primary_group, nentry->name); if(pmatch.rm_eo - pmatch.rm_so != nmatch.rm_eo - nmatch.rm_so || (pmatch.rm_eo != pmatch.rm_so && strncmp(pentry->name + pmatch.rm_so, nentry->name + nmatch.rm_so, pmatch.rm_eo - pmatch.rm_so + 1U) != 0)) return pos; } break; case SK_BY_TARGET: if((nentry->type == FT_LINK) != (pentry->type == FT_LINK)) { /* One of the entries is not a link. */ return pos; } if(nentry->type == FT_LINK) { /* Both entries are symbolic links. */ char full_path[PATH_MAX]; char nlink[PATH_MAX], plink[PATH_MAX]; get_full_path_of(nentry, sizeof(full_path), full_path); if(get_link_target(full_path, nlink, sizeof(nlink)) != 0) { return pos; } get_full_path_of(pentry, sizeof(full_path), full_path); if(get_link_target(full_path, plink, sizeof(plink)) != 0) { return pos; } if(stroscmp(nlink, plink) != 0) { return pos; } } break; case SK_BY_NAME: if(strncmp(pentry->name, nentry->name, char_width) != 0) return pos; break; case SK_BY_INAME: if((wchar_t)towupper(get_first_wchar(nentry->name)) != ch) return pos; break; case SK_BY_SIZE: if(nentry->size != pentry->size) return pos; break; case SK_BY_NITEMS: if(entry_get_nitems(view, nentry) != entry_get_nitems(view, pentry)) return pos; break; case SK_BY_TIME_ACCESSED: if(nentry->atime != pentry->atime) return pos; break; case SK_BY_TIME_CHANGED: if(nentry->ctime != pentry->ctime) return pos; break; case SK_BY_TIME_MODIFIED: if(nentry->mtime != pentry->mtime) return pos; break; case SK_BY_DIR: if(is_dir != fentry_is_dir(nentry)) { return pos; } break; case SK_BY_TYPE: if(get_type_str(nentry->type) != type_str) { return pos; } break; #ifndef _WIN32 case SK_BY_GROUP_NAME: case SK_BY_GROUP_ID: if(nentry->gid != pentry->gid) return pos; break; case SK_BY_OWNER_NAME: case SK_BY_OWNER_ID: if(nentry->uid != pentry->uid) return pos; break; case SK_BY_MODE: if(nentry->mode != pentry->mode) return pos; break; case SK_BY_PERMISSIONS: { char nperms[16]; get_perm_string(nperms, sizeof(nperms), nentry->mode); if(strcmp(nperms, perms) != 0) { return pos; } break; } case SK_BY_NLINKS: if(nentry->nlinks != pentry->nlinks) { return pos; } break; #endif } /* Id sorting is builtin only and is defined outside SortingKey * enumeration. */ if((int)sorting_key == SK_BY_ID) { if(nentry->id != pentry->id) { return pos; } } } return pos; } /* Finds pointer to the beginning of the last extension of the file name. * Returns the pointer, which might point to the NUL byte if there are no * extensions. */ static const char * get_last_ext(const char name[]) { const char *const ext = strrchr(name, '.'); return (ext == NULL) ? (name + strlen(name)) : (ext + 1); } int flist_find_dir_group(const FileView *view, int next) { const int correction = next ? -1 : 0; const int lb = correction; const int ub = view->list_rows + correction; const int inc = next ? +1 : -1; int pos = curr_view->list_pos; dir_entry_t *pentry = &curr_view->dir_entry[pos]; const int is_dir = fentry_is_dir(pentry); while(pos > lb && pos < ub) { dir_entry_t *nentry; pos += inc; nentry = &curr_view->dir_entry[pos]; if(is_dir != fentry_is_dir(nentry)) { break; } } return pos; } int flist_first_sibling(const FileView *view) { const int parent = view->list_pos - view->dir_entry[view->list_pos].child_pos; return (parent == view->list_pos ? 0 : parent + 1); } int flist_last_sibling(const FileView *view) { int pos = view->list_pos - view->dir_entry[view->list_pos].child_pos; if(pos == view->list_pos) { /* For top-level entry, find the last top-level entry. */ pos = view->list_rows - 1; while(view->dir_entry[pos].child_pos != 0) { pos -= view->dir_entry[pos].child_pos; } } else { /* For non-top-level entry, go to last tree item and go up until our * child. */ const int parent = pos; pos = parent + view->dir_entry[parent].child_count; while(pos - view->dir_entry[pos].child_pos != parent) { pos -= view->dir_entry[pos].child_pos; } } return pos; }