Beispiel #1
0
int
flist_prev_dir_sibling(const FileView *view)
{
	int pos = view->list_pos;
	/* Determine original parent (-1 for top-most entry). */
	const int parent = view->dir_entry[pos].child_pos == 0
	                 ? -1
	                 : pos - view->dir_entry[pos].child_pos;
	--pos;
	while(pos > parent)
	{
		dir_entry_t *const e = &view->dir_entry[pos];
		const int p = (e->child_pos == 0) ? -1 : (pos - e->child_pos);
		/* If we find ourselves deeper than originally, just go up one level. */
		if(p != parent)
		{
			pos = p;
			continue;
		}

		/* We're looking for directories. */
		if(fentry_is_dir(e))
		{
			break;
		}
		/* We're on a file on the same level. */
		--pos;
	}
	return (pos > parent ? pos : view->list_pos);
}
Beispiel #2
0
int
local_filter_matches(view_t *view, const dir_entry_t *entry)
{
	/* FIXME: some very long file names won't be matched against some regexps. */
	char name_with_slash[NAME_MAX + 1 + 1];
	const char *filename = entry->name;
	if(fentry_is_dir(entry))
	{
		append_slash(filename, name_with_slash, sizeof(name_with_slash));
		filename = name_with_slash;
	}

	return filter_matches(&view->local_filter.filter, filename) != 0;
}
Beispiel #3
0
/* zap_entries() filter to filter-out files that match filter passed in the
 * arg. */
static int
is_newly_filtered(view_t *view, const dir_entry_t *entry, void *arg)
{
	filter_t *const filter = arg;

	/* FIXME: some very long file names won't be matched against some regexps. */
	char name_with_slash[NAME_MAX + 1 + 1];
	const char *filename = entry->name;

	if(fentry_is_dir(entry))
	{
		append_slash(filename, name_with_slash, sizeof(name_with_slash));
		filename = name_with_slash;
	}

	return filter_matches(filter, filename) == 0;
}
Beispiel #4
0
/* Fills the list with entries of the view in hierarchical order (pre-order tree
 * traversal). */
static void
list_view_entries(const FileView *view, strlist_t *list)
{
	int i;

	fsdata_t *const tree = fsdata_create(0, 0);

	for(i = 0; i < view->list_rows; ++i)
	{
		if(!fentry_is_dir(&view->dir_entry[i]))
		{
			char full_path[PATH_MAX];
			void *data = &view->dir_entry[i];
			get_full_path_of(&view->dir_entry[i], sizeof(full_path), full_path);
			fsdata_set(tree, full_path, &data, sizeof(data));
		}
	}

	fsdata_traverse(tree, &append_valid_nodes, list);

	fsdata_free(tree);
}
Beispiel #5
0
int
flist_next_dir_sibling(const FileView *view)
{
	int pos = view->list_pos;
	const int parent = view->dir_entry[pos].child_pos == 0
	                 ? -1
	                 : pos - view->dir_entry[pos].child_pos;
	const int past_end = parent == -1
	                   ? view->list_rows
	                   : parent + view->dir_entry[parent].child_count;
	pos += view->dir_entry[pos].child_count + 1;
	while(pos < past_end)
	{
		dir_entry_t *const e = &view->dir_entry[pos];
		if(fentry_is_dir(e))
		{
			break;
		}
		/* Skip over whole sub-tree. */
		pos += e->child_count + 1;
	}
	return (pos < past_end ? pos : view->list_pos);
}
Beispiel #6
0
/* initializes file attributes array */
static void
get_attrs(void)
{
	int i;
	DWORD attributes;
	DWORD diff = 0;
	dir_entry_t *entry = NULL;
	int first = 1;

	file_is_dir = 0;
	while(iter_selection_or_current(view, &entry))
	{
		if(first)
		{
			attributes = entry->attrs;
			first = 0;
		}

		diff |= (entry->attrs ^ attributes);
		file_is_dir |= fentry_is_dir(entry);
	}
	if(first)
	{
		show_error_msg("Attributes", "No files to process");
		return;
	}

	/* FIXME: allow setting attributes recursively. */
	file_is_dir = 0;

	memset(attrs, 0, sizeof(attrs));
	for(i = 0; i < ATTR_COUNT; ++i)
	{
		attrs[i] = !(diff & attr_list[i]) ? (int)(attributes & attr_list[i]) : -1;
	}
	memcpy(origin_attrs, attrs, sizeof(attrs));
}
Beispiel #7
0
int
find_pattern(FileView *view, const char pattern[], int backward, int move,
		int *const found, int print_errors)
{
	int cflags;
	int nmatches = 0;
	regex_t re;
	int err;
	FileView *other;

	if(move && cfg.hl_search)
	{
		flist_sel_stash(view);
	}

	reset_search_results(view);

	/* We at least could wipe out previous search results, so schedule a
	 * redraw. */
	ui_view_schedule_redraw(view);

	if(pattern[0] == '\0')
	{
		*found = 1;
		return 0;
	}

	*found = 0;

	cflags = get_regexp_cflags(pattern);
	if((err = regcomp(&re, pattern, cflags)) == 0)
	{
		int i;
		for(i = 0; i < view->list_rows; ++i)
		{
			regmatch_t matches[1];
			dir_entry_t *const entry = &view->dir_entry[i];
			const char *name = entry->name;
			char *free_this = NULL;

			if(is_parent_dir(name))
			{
				continue;
			}

			if(fentry_is_dir(entry))
			{
				free_this = format_str("%s/", name);
				name = free_this;
			}

			if(regexec(&re, name, 1, matches, 0) != 0)
			{
				free(free_this);
				continue;
			}
			free(free_this);

			entry->search_match = nmatches + 1;
			entry->match_left = matches[0].rm_so;
			entry->match_right = matches[0].rm_eo;
			if(cfg.hl_search)
			{
				entry->selected = 1;
				++view->selected_files;
			}
			++nmatches;
		}
		regfree(&re);
	}
	else
	{
		if(print_errors)
		{
			status_bar_errorf("Regexp error: %s", get_regexp_error(err, &re));
		}
		regfree(&re);
		return -1;
	}

	other = (view == &lwin) ? &rwin : &lwin;
	if(other->matches != 0 && strcmp(other->last_search, pattern) != 0)
	{
		other->last_search[0] = '\0';
		ui_view_reset_search_highlight(other);
	}
	view->matches = nmatches;
	copy_str(view->last_search, sizeof(view->last_search), pattern);

	view->matches = nmatches;
	if(nmatches > 0)
	{
		const int was_found = move ? goto_search_match(view, backward) : 1;
		*found = was_found;

		if(!cfg.hl_search)
		{
			if(print_errors)
			{
				print_result(view, was_found, backward);
			}
			return 1;
		}
		return 0;
	}
	else
	{
		if(print_errors)
		{
			print_search_fail_msg(view, backward);
		}
		return 1;
	}
}
Beispiel #8
0
/* Copies/moves elements of the unfiltered list into dir_entry list.  add
 * parameter controls whether entries matching filter are copied into dir_entry
 * list.  clear parameter controls whether entries not matching filter are
 * cleared in unfiltered list.  Returns zero unless addition is performed in
 * which case can return non-zero when all files got filtered out. */
static int
update_filtering_lists(view_t *view, int add, int clear)
{
	/* filters_drop_temporaries() is a similar function. */

	size_t i;
	size_t list_size = 0U;
	dir_entry_t *parent_entry = NULL;
	int parent_added = 0;

	for(i = 0; i < view->local_filter.unfiltered_count; ++i)
	{
		/* FIXME: some very long file names won't be matched against some
		 * regexps. */
		char name_with_slash[NAME_MAX + 1 + 1];

		dir_entry_t *const entry = &view->local_filter.unfiltered[i];
		const char *name = entry->name;

		if(is_parent_dir(name))
		{
			if(entry->child_pos == 0)
			{
				parent_entry = entry;
				if(add && cfg_parent_dir_is_visible(is_root_dir(view->curr_dir)))
				{
					(void)add_dir_entry(&view->dir_entry, &list_size, entry);

					parent_added = 1;
				}
				continue;
			}
			else if(!filter_is_empty(&view->local_filter.filter))
			{
				if(clear)
				{
					fentry_free(view, entry);
				}
				continue;
			}
		}

		if(fentry_is_dir(entry))
		{
			append_slash(name, name_with_slash, sizeof(name_with_slash));
			name = name_with_slash;
		}

		/* tag links to position of nodes passed through filter in list of visible
		 * files.  Nodes that didn't pass have -1. */
		entry->tag = -1;
		if(filter_matches(&view->local_filter.filter, name) != 0)
		{
			if(add)
			{
				dir_entry_t *e = add_dir_entry(&view->dir_entry, &list_size, entry);
				if(e != NULL)
				{
					entry->tag = list_size - 1U;
					/* We basically grow the tree node by node while performing
					 * reparenting. */
					reparent_tree_node(entry, e);
				}
			}
		}
		else
		{
			if(clear)
			{
				fentry_free(view, entry);
			}
		}
	}

	if(clear)
	{
		/* XXX: the check of name pointer is horrible, but is needed to prevent
		 *      freeing of entry in use. */
		if(!parent_added && parent_entry != NULL && list_size != 0U &&
				view->dir_entry[0].name != parent_entry->name)
		{
			fentry_free(view, parent_entry);
		}
	}
	if(add)
	{
		view->list_rows = list_size;
		view->filtered = view->local_filter.prefiltered_count
		               + view->local_filter.unfiltered_count - list_size;
		ensure_filtered_list_not_empty(view, parent_entry);
		return list_size == 0U
		    || (list_size == 1U && parent_added &&
						(filter_matches(&view->local_filter.filter, "../") == 0));
	}
	return 0;
}
Beispiel #9
0
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);
}
Beispiel #10
0
/* Expands macros in the *format string advancing the pointer as it goes.  The
 * opt represents conditional expression state, should be zero for non-recursive
 * calls.  Returns newly allocated string, which should be freed by the
 * caller. */
static LineWithAttrs
parse_view_macros(view_t *view, const char **format, const char macros[],
		int opt)
{
	const dir_entry_t *const curr = get_current_entry(view);
	LineWithAttrs result = { .line = strdup(""), .attrs = strdup("") };
	char c;
	int nexpansions = 0;
	int has_expander = 0;

	if(curr == NULL)
	{
		return result;
	}

	while((c = **format) != '\0')
	{
		size_t width = 0;
		int left_align = 0;
		char buf[PATH_MAX + 1];
		const char *const next = ++*format;
		int skip, ok;

		if(c != '%' ||
				(!char_is_one_of(macros, *next) && !isdigit(*next) &&
				 (*next != '=' || has_expander)))
		{
			if(strappendch(&result.line, &result.line_len, c) != 0)
			{
				break;
			}
			continue;
		}

		if(*next == '=')
		{
			(void)sync_attrs(&result, 0);

			if(strappend(&result.line, &result.line_len, "%=") != 0 ||
					strappendch(&result.attrs, &result.attrs_len, '=') != 0)
			{
				break;
			}
			++*format;
			has_expander = 1;
			continue;
		}

		if(*next == '-')
		{
			left_align = 1;
			++*format;
		}

		while(isdigit(**format))
		{
			width = width*10 + *(*format)++ - '0';
		}
		c = *(*format)++;

		skip = 0;
		ok = 1;
		buf[0] = '\0';
		switch(c)
		{
			case 'a':
				friendly_size_notation(get_free_space(curr_view->curr_dir), sizeof(buf),
						buf);
				break;
			case 't':
				format_entry_name(curr, NF_FULL, sizeof(buf), buf);
				break;
			case 'T':
				if(curr->type == FT_LINK)
				{
					char full_path[PATH_MAX + 1];
					char link_path[PATH_MAX + 1];  //add by sim1
					get_full_path_of(curr, sizeof(full_path), full_path);
					//mod by sim1 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
					if(get_link_target(full_path, link_path, sizeof(link_path)) != 0)
					{
						copy_str(buf, sizeof(buf), "Failed to resolve link");
					}
					else
					{
						snprintf(buf, sizeof(buf), " -> %s", link_path);
					}
					//mod by sim1 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
				}
				break;
			case 'f':
				get_short_path_of(view, curr, NF_FULL, 0, sizeof(buf), buf);
				break;
			case 'A':
#ifndef _WIN32
				get_perm_string(buf, sizeof(buf), curr->mode);
#else
				copy_str(buf, sizeof(buf), attr_str_long(curr->attrs));
#endif
				break;
			case 'u':
				get_uid_string(curr, 0, sizeof(buf), buf);
				break;
			case 'g':
				get_gid_string(curr, 0, sizeof(buf), buf);
				break;
			case 's':
				friendly_size_notation(fentry_get_size(view, curr), sizeof(buf), buf);
				break;
			//add by sim1 ************************************************
			case 'r':
				{
					char path[PATH_MAX] = {0};
					get_full_path_at(view, view->list_pos, sizeof(path), path);
					(void)get_rating_string(buf, sizeof(buf), path);
				}
				break;
			case 'n':
				{
					int nitems = !fentry_is_dir(curr) ? 0 : (int)fentry_get_nitems(view, curr);
					snprintf(buf, sizeof(buf), "%d", nitems);
				}
				break;
			//add by sim1 ************************************************
			case 'E':
				{
					uint64_t size = 0U;

					typedef int (*iter_f)(view_t *view, dir_entry_t **entry);
					/* No current element for visual mode, since it can contain truly
					 * empty selection when cursor is on ../ directory. */
					iter_f iter = vle_mode_is(VISUAL_MODE) ? &iter_selected_entries
					                                       : &iter_selection_or_current;

					dir_entry_t *entry = NULL;
					while(iter(view, &entry))
					{
						size += fentry_get_size(view, entry);
					}

					friendly_size_notation(size, sizeof(buf), buf);
				}
				break;
			case 'd':
				{
					struct tm *tm_ptr = localtime(&curr->mtime);
					strftime(buf, sizeof(buf), cfg.time_format, tm_ptr);
				}
				break;
			case '-':
			case 'x':
				skip = expand_num(buf, sizeof(buf), view->filtered);
				break;
			case 'l':
				skip = expand_num(buf, sizeof(buf), view->list_pos + 1);
				break;
			case 'L':
				skip = expand_num(buf, sizeof(buf), view->list_rows + view->filtered);
				break;
			case 'S':
				skip = expand_num(buf, sizeof(buf), view->list_rows);
				break;
			case '%':
				copy_str(buf, sizeof(buf), "%");
				break;
			case 'z':
				copy_str(buf, sizeof(buf), get_tip());
				break;
			case 'D':
				if(curr_stats.number_of_windows == 1)
				{
					view_t *const other = (view == curr_view) ? other_view : curr_view;
					//mod by sim1
					//copy_str(buf, sizeof(buf), replace_home_part(other->curr_dir));
					snprintf(buf, sizeof(buf), " ‖ %s", replace_home_part(other->curr_dir));
				}
				break;
			case '[':
				{
					LineWithAttrs opt = parse_view_macros(view, format, macros, 1);
					copy_str(buf, sizeof(buf), opt.line);
					free(opt.line);

					char *attrs = opt.attrs;
					if(sync_attrs(&result, 0) && opt.attrs_len > 0U)
					{
						if(*attrs != ' ')
						{
							result.attrs[result.attrs_len - 1U] = *attrs;
						}
						++attrs;
					}
					strappend(&result.attrs, &result.attrs_len, attrs);
					free(opt.attrs);
					break;
				}
			case ']':
				if(opt)
				{
					if(nexpansions == 0)
					{
						replace_string(&result.line, "");
						replace_string(&result.attrs, "");
						result.line_len = 0U;
						result.attrs_len = 0U;
					}
					if(sync_attrs(&result, 0))
					{
						result.attrs[--result.attrs_len] = '\0';
					}
					return result;
				}

				LOG_INFO_MSG("Unmatched %%]");
				ok = 0;
				break;
			case '{':
				{
					/* Try to find matching closing bracket
					 * TODO: implement the way to escape it, so that the expr may contain
					 * closing brackets */
					const char *e = strchr(*format, '}');
					char *expr = NULL, *resstr = NULL;
					var_t res = var_false();
					ParsingErrors parsing_error;

					/* If there's no matching closing bracket, just add the opening one
					 * literally */
					if(e == NULL)
					{
						ok = 0;
						break;
					}

					/* Create a NULL-terminated copy of the given expr.
					 * TODO: we could temporarily use buf for that, to avoid extra
					 * allocation, but explicitly named variable reads better. */
					expr = calloc(e - (*format) + 1 /* NUL-term */, 1);
					memcpy(expr, *format, e - (*format));

					/* Try to parse expr, and convert the res to string if succeed. */
					parsing_error = parse(expr, 0, &res);
					if(parsing_error == PE_NO_ERROR)
					{
						resstr = var_to_str(res);
					}

					if(resstr != NULL)
					{
						copy_str(buf, sizeof(buf), resstr);
					}
					else
					{
						copy_str(buf, sizeof(buf), "<Invalid expr>");
					}

					var_free(res);
					free(resstr);
					free(expr);

					*format = e + 1 /* closing bracket */;
				}
				break;
			case '*':
				if(width > 9)
				{
					snprintf(buf, sizeof(buf), "%%%d*", (int)width);
					width = 0;
					break;
				}
				(void)sync_attrs(&result, 1);
				result.attrs[result.attrs_len - 1] = '0' + width;
				width = 0;
				break;

			default:
				LOG_INFO_MSG("Unexpected %%-sequence: %%%c", c);
				ok = 0;
				break;
		}

		if(char_is_one_of("tTAugsEd", c) && fentry_is_fake(curr))
		{
			buf[0] = '\0';
		}

		if(!ok)
		{
			*format = next;
			if(strappendch(&result.line, &result.line_len, '%') != 0)
			{
				break;
			}
			continue;
		}

		check_expanded_str(buf, skip, &nexpansions);
		stralign(buf, width, ' ', left_align);

		if(strappend(&result.line, &result.line_len, buf) != 0)
		{
			break;
		}
	}

	/* Unmatched %[. */
	if(opt)
	{
		(void)strprepend(&result.line, &result.line_len, "%[");
	}

	if(sync_attrs(&result, 0))
	{
		result.attrs[--result.attrs_len] = '\0';
	}
	return result;
}

/* Makes sure that result->attrs has at least as many elements as result->line
 * contains characters + extra_width.  Returns non-zero if result->attrs has
 * extra characters compared to result->line. */
static int
sync_attrs(LineWithAttrs *result, int extra_width)
{
	const size_t nchars = utf8_strsw(result->line) + extra_width;
	if(result->attrs_len < nchars)
	{
		char *const new_attrs = format_str("%s%*s", result->attrs,
				(int)(nchars - result->attrs_len), "");
		free(result->attrs);
		result->attrs = new_attrs;
		result->attrs_len = nchars;
	}
	return (result->attrs_len > nchars);
}

/* Prints number into the buffer.  Returns non-zero if numeric value is
 * "empty" (zero). */
static int
expand_num(char buf[], size_t buf_len, int val)
{
	snprintf(buf, buf_len, "%d", val);
	return (val == 0);
}
Beispiel #11
0
void
enter_attr_mode(FileView *active_view)
{
	mode_t fmode = 0;
	mode_t diff;
	uid_t uid = geteuid();
	dir_entry_t *entry;
	int first;

	if(curr_stats.load_stage < 2)
		return;

	view = active_view;
	memset(perms, 0, sizeof(perms));

	first = 1;
	file_is_dir = 0;
	diff = 0;
	entry = NULL;
	while(iter_selection_or_current(view, &entry))
	{
		if(first)
		{
			fmode = entry->mode;
			first = 0;
		}

		diff |= (entry->mode ^ fmode);
		file_is_dir |= fentry_is_dir(entry);

		if(uid != 0 && entry->uid != uid)
		{
			show_error_msgf("Access error", "You are not owner of %s", entry->name);
			return;
		}
	}
	if(first)
	{
		show_error_msg("Permissions", "No files to process");
		return;
	}

	vle_mode_set(ATTR_MODE, VMT_SECONDARY);
	clear_input_bar();
	curr_stats.use_input_bar = 0;

	perms[0] = !(diff & S_IRUSR) ? (int)(fmode & S_IRUSR) : -1;
	perms[1] = !(diff & S_IWUSR) ? (int)(fmode & S_IWUSR) : -1;
	perms[2] = !(diff & S_IXUSR) ? (int)(fmode & S_IXUSR) : -1;
	perms[3] = !(diff & S_ISUID) ? (int)(fmode & S_ISUID) : -1;
	perms[4] = !(diff & S_IRGRP) ? (int)(fmode & S_IRGRP) : -1;
	perms[5] = !(diff & S_IWGRP) ? (int)(fmode & S_IWGRP) : -1;
	perms[6] = !(diff & S_IXGRP) ? (int)(fmode & S_IXGRP) : -1;
	perms[7] = !(diff & S_ISGID) ? (int)(fmode & S_ISGID) : -1;
	perms[8] = !(diff & S_IROTH) ? (int)(fmode & S_IROTH) : -1;
	perms[9] = !(diff & S_IWOTH) ? (int)(fmode & S_IWOTH) : -1;
	perms[10] = !(diff & S_IXOTH) ? (int)(fmode & S_IXOTH) : -1;
	perms[11] = !(diff & S_ISVTX) ? (int)(fmode & S_ISVTX) : -1;
	adv_perms[0] = 0;
	adv_perms[1] = 0;
	adv_perms[2] = 0;
	memcpy(origin_perms, perms, sizeof(perms));

	top = 3;
	bottom = file_is_dir ? 18 : 16;
	curr = 3;
	permnum = 0;
	step = 1;
	while(perms[permnum] < 0 && curr <= bottom)
	{
		inc_curr();
		permnum++;
	}

	if(curr > bottom)
	{
		show_error_msg("Permissions change error",
				"Selected files have no common access state");
		leave_attr_mode();
		return;
	}

	col = 9;
	changed = 0;
	redraw_attr_dialog();
}
Beispiel #12
0
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;
}
Beispiel #13
0
static int
sort_dir_list(const void *one, const void *two)
{
	/* TODO: refactor this function sort_dir_list(). */

	int retval;
	const dir_entry_t *const first = one;
	const dir_entry_t *const second = two;

	const int first_is_dir = fentry_is_dir(first);
	const int second_is_dir = fentry_is_dir(second);

	if(first_is_dir && is_parent_dir(first->name))
	{
		return -1;
	}
	if(second_is_dir && is_parent_dir(second->name))
	{
		return 1;
	}

	retval = 0;
	switch(sort_type)
	{
		char *pfirst, *psecond;

		case SK_BY_NAME:
		case SK_BY_INAME:
			if(custom_view)
			{
				retval = compare_entry_names(first, second, sort_type == SK_BY_INAME);
			}
			else
			{
				retval = compare_full_file_names(first->name, second->name,
						sort_type == SK_BY_INAME);
			}
			break;

		case SK_BY_DIR:
			if(first_is_dir != second_is_dir)
			{
				retval = first_is_dir ? -1 : 1;
			}
			break;

		case SK_BY_TYPE:
			retval = strcmp(get_type_str(first->type), get_type_str(second->type));
			break;

		case SK_BY_FILEEXT:
		case SK_BY_EXTENSION:
			pfirst = strrchr(first->name,  '.');
			psecond = strrchr(second->name, '.');

			if(first_is_dir && second_is_dir && sort_type == SK_BY_FILEEXT)
			{
				retval = compare_file_names(first->name, second->name, 0);
			}
			else if(first_is_dir != second_is_dir && sort_type == SK_BY_FILEEXT)
			{
				retval = first_is_dir ? -1 : 1;
			}
			else if(pfirst && psecond)
			{
				if(pfirst == first->name && psecond != second->name)
				{
					retval = -1;
				}
				else if(pfirst != first->name && psecond == second->name)
				{
					retval = 1;
				}
				else
				{
					retval = compare_file_names(++pfirst, ++psecond, 0);
				}
			}
			else if(pfirst || psecond)
				retval = pfirst ? -1 : 1;
			else
				retval = compare_file_names(first->name, second->name, 0);
			break;

		case SK_BY_SIZE:
			retval = compare_file_sizes(first, second);
			break;

		case SK_BY_NITEMS:
			retval = compare_item_count(first, first_is_dir, second, second_is_dir);
			break;

		case SK_BY_GROUPS:
			retval = compare_group(first->name, second->name, sort_data);
			break;

		case SK_BY_TARGET:
			retval = compare_targets(first, second);
			break;

		case SK_BY_TIME_MODIFIED:
			retval = first->mtime - second->mtime;
			break;

		case SK_BY_TIME_ACCESSED:
			retval = first->atime - second->atime;
			break;

		case SK_BY_TIME_CHANGED:
			retval = first->ctime - second->ctime;
			break;

#ifndef _WIN32
		case SK_BY_MODE:
			retval = first->mode - second->mode;
			break;

		case SK_BY_INODE:
			retval = first->inode - second->inode;
			break;

		case SK_BY_OWNER_NAME: /* FIXME */
		case SK_BY_OWNER_ID:
			retval = first->uid - second->uid;
			break;

		case SK_BY_GROUP_NAME: /* FIXME */
		case SK_BY_GROUP_ID:
			retval = first->gid - second->gid;
			break;

		case SK_BY_PERMISSIONS:
			{
				char first_perm[11], second_perm[11];
				get_perm_string(first_perm, sizeof(first_perm), first->mode);
				get_perm_string(second_perm, sizeof(second_perm), second->mode);
				retval = strcmp(first_perm, second_perm);
			}
			break;

		case SK_BY_NLINKS:
			retval = first->nlinks - second->nlinks;
			break;
#endif
	}

	if(retval == 0)
	{
		retval = first->tag - second->tag;
	}
	else if(sort_descending)
	{
		retval = -retval;
	}

	return retval;
}