summaryrefslogtreecommitdiffstats
path: root/COPYING
AgeCommit message (Expand)Author
2005-09-10[PATCH] update FSF address in COPYINGPekka J Enberg
2005-04-16Linux-2.6.12-rc2Linus Torvalds
authorxaizek <xaizek@openmailbox.org>2017-06-20 16:56:42 +0300
committerxaizek <xaizek@openmailbox.org>2017-06-20 16:56:42 +0300
commitdf6ca643ed15e410512c4a36c97539378d532ee7 (patch)
tree6d1df14dd5bdf19cd85f39401eb4d062d20eb522
parent88066723fcea2fa7b0fef61d514914e1f61a5cd3 (diff)
parenta82ddba30b409df80c4414c7aa8f06407252a75a (diff)
Merge branch 'make-filter-take-matcher'
Changed :filter command to accept pattern (//, ////, {} or {{}}). Closes #268 on GitHub. Thanks to rbong. Update version number in .travis.yml. Update year in the README.
Diffstat
-rw-r--r--.travis.yml2
-rw-r--r--ChangeLog5
-rw-r--r--README2
-rw-r--r--data/man/vifm.149
-rw-r--r--data/vim/doc/app/vifm-app.txt46
-rw-r--r--data/vim/syntax/vifm.vim20
-rw-r--r--src/cfg/info.c36
-rw-r--r--src/cmd_handlers.c129
-rw-r--r--src/event_loop.c2
-rw-r--r--src/filelist.c5
-rw-r--r--src/filelist.h2
-rw-r--r--src/filtering.c45
-rw-r--r--src/modes/cmdline.c3
-rw-r--r--src/ui/ui.h2
-rw-r--r--src/utils/matcher.c57
-rw-r--r--src/utils/matcher.h8
-rw-r--r--src/utils/regexp.h5
-rw-r--r--src/utils/str.c13
-rw-r--r--src/utils/str.h4
-rw-r--r--tests/fileops/utils.c8
-rw-r--r--tests/misc/chase_links.c8
-rw-r--r--tests/misc/cmdline_editing.c10
-rw-r--r--tests/misc/commands.c125
-rw-r--r--tests/misc/commands_filter.c181
-rw-r--r--tests/misc/filtering.c57
-rw-r--r--tests/misc/flist_custom.c22
-rw-r--r--tests/misc/flist_reload.c7
-rw-r--r--tests/misc/flist_tree.c20
-rw-r--r--tests/misc/navigation.c8
-rw-r--r--tests/misc/utils.c21
-rw-r--r--tests/misc/utils.h6
-rw-r--r--tests/misc/vifminfo.c11
-rw-r--r--tests/test-data/syntax-highlight/syntax.vifm6
-rw-r--r--tests/utils/matcher.c58
34 files changed, 634 insertions, 349 deletions
diff --git a/.travis.yml b/.travis.yml
index 29ac9e4dc..43ab9dd53 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -20,7 +20,7 @@ addons:
coverity_scan:
project:
name: vifm/vifm
- version: 0.8.2+
+ version: 0.9+
description: "TUI file manager with vi like key bindings."
notification_email: xaizek@openmailbox.org
build_command_prepend: ./fix-timestamps fast && ./configure
diff --git a/ChangeLog b/ChangeLog
index 54092f3fa..2a39f8c7f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+0.9 to current
+
+ Changed :filter command to accept pattern (//, ////, {} or {{}}). Thanks
+ to rbong.
+
0.9-beta to 0.9
Escape $ and ` in %" macros on *nix systems. Thanks to filterfalse.
diff --git a/README b/README
index 745f84934..404d37bc3 100644
--- a/README
+++ b/README
@@ -1,5 +1,5 @@
Vifm - vi[m] like file manager
-2001 - 2016
+2001 - 2017
Updated: 18 June, 2017
Version: 0.9
diff --git a/data/man/vifm.1 b/data/man/vifm.1
index 1158f2d2e..787dc0350 100644
--- a/data/man/vifm.1
+++ b/data/man/vifm.1
@@ -433,6 +433,8 @@ to a directory name before testing whether it matches the filter. Examples:
" filter files and directories which names end with '.o'
:filter /^.*\\.o\\/?$/
.EE
+
+Note: vifm uses extended regular expressions.
.TP
.BI za
toggle visibility of dot files.
@@ -1845,32 +1847,32 @@ list (in menu mode) currently registered patterns that match specified filename.
.TP
.BI " :filter"
.TP
-.BI ":filter[!] regular_expression_pattern"
-.TP
-.BI ":filter[!] /regular_expression_pattern/[flags]"
-will filter all the files out of the directory listing that match the regular
-expression. Using second variant you can use the bar ('|') symbol without
-escaping. Empty regular expression (specified by //, "" or '') means using of
-the last search pattern. Use '!' to control state of filter inversion after
-updating filter value (also see 'cpoptions' description). Filter is matched
-case sensitively on *nix and case insensitively on Windows.
+.BI ":filter[!] {pattern}"
+filter files matching the pattern out of directory listings. '!' controls state
+of filter inversion after updating filter value (see also 'cpoptions'
+description). Filter is matched case sensitively on *nix and case insensitively
+on Windows. See "File Filters" and "Patterns" sections.
+
+Example:
+.EX
-Supported flags:
- \- "i" makes filter case insensitive;
- \- "I" makes filter case sensitive.
+ " filter all files ending in .o from the filelist.
+ :filter /\.o$/
+.EE
-Flags might be repeated multiple times, later ones win (e.g. "iiiI" is
-equivalent to "I" and "IiIi" is the same as "i").
+.TP
+.BI ":filter[!] {empty-pattern}"
+same as above, but use last search pattern as pattern value.
+Example:
.EX
-" filter all files ending in .o from the filelist.
-:filter /\.o$/
+
+ :filter //I
.EE
-Note: vifm uses extended regular expressions.
.TP
.BI ":filter"
-reset filter (set it to empty string) and show all files.
+reset filter (set it to an empty string) and show all files.
.TP
.BI ":filter!"
same as :invert.
@@ -3178,7 +3180,13 @@ There are six possible ways to write a single pattern:
undecorated-pattern
.RE
-To combine several patterns (AND them), make sure you're using of the first
+Flags of regular expressions mean the following:
+ - "i" makes filter case insensitive;
+ - "I" makes filter case sensitive.
+They can be repeated multiple times, but the later one takes precedence (e.g.
+"iiiI" is equivalent to "I" and "IiIi" is the same as "i").
+
+To combine several patterns (AND them), make sure you're using one of the first
five forms and write patterns one after another, like this:
.EX
<text/plain>{*.vifm}
@@ -3200,7 +3208,8 @@ The last form is implicitly refers to one of others. :highlight does not accept
undecorated form, while :filetype, :filextype, :fileviewer, :select, :unselect
and 'classify' treat it as list of name globs.
-Regular expression patterns are case insensitive by default.
+Regular expression patterns are case insensitive by default, see description
+of commands, which might override default behaviour.
"Globs" section below provides short overview of globs and some important points
that one needs to know about them.
diff --git a/data/vim/doc/app/vifm-app.txt b/data/vim/doc/app/vifm-app.txt
index cccd13885..153579114 100644
--- a/data/vim/doc/app/vifm-app.txt
+++ b/data/vim/doc/app/vifm-app.txt
@@ -1583,25 +1583,24 @@ The builtin commands are:
filename.
*vifm-:filter*
-:filter[!] regular_expression
-:filter[!] /regular_expression/[flags]
- filter files matching the pattern out of directory listing. Use
- :filter to show all files. Using second variant you can use the bar ('|')
- symbol without escaping. Empty regular expression (specified by //, "" or
- '') means using of the last search pattern. Use '!' to control state of
- filter inversion after updating filter value (see |vifm-cpo-f|). Filter is
- matched case sensitively on *nix and case insensitively on Windows.
- See |vifm-filters|.
-
- Supported flags:
- - "i" makes filter case insensitive;
- - "I" makes filter case sensitive.
-
- Flags might be repeated multiple times, later ones win (e.g. "iiiI" is
- equivalent to "I" and "IiIi" is the same as "i").
+:filter[!] {pattern}
+ filter files matching the pattern out of directory listings. '!'
+ controls state of filter inversion after updating filter value
+ (see |vifm-cpo-f|). Filter is matched case sensitively on *nix and
+ case insensitively on Windows. See |vifm-filters| and |vifm-patterns|.
+
+ Example: >
+ " filter all files ending in .o from the filelist.
+ :filter /\.o$/
+
+:filter[!] {empty-pattern}
+ same as above, but use last search pattern as pattern value.
+
+ Example: >
+ :filter //I
:filter
- reset filter (set it to empty string) and show all files.
+ reset filter (set it to an empty string) and show all files.
:filter!
same as |vifm-:invert|.
:filter?
@@ -2626,8 +2625,14 @@ There are six possible ways to write a single pattern:
5. [!]<comma-separated-mime-type-globs>
6. undecorated-pattern
-To combine several patterns (AND them), make sure you're using of the first
-five forms and write patterns one after another, like this: >
+Flags of regular expressions mean the following:
+ - "i" makes filter case insensitive;
+ - "I" makes filter case sensitive.
+They can be repeated multiple times, but the later one takes precedence (e.g.
+"iiiI" is equivalent to "I" and "IiIi" is the same as "i").
+
+To combine several patterns (AND them), make sure you're using one of the
+first five forms and write patterns one after another, like this: >
<text/plain>{*.vifm}
Mind that if you make a mistake the whole string will be treated as the sixth
form.
@@ -2645,7 +2650,8 @@ does not accept undecorated form, while |vifm-:filetype|, |vifm-:filextype|,
|vifm-:fileviewer|, |vifm-:select|, |vifm-:unselect| and |vifm-'classify'|
treat it as list of name globs.
-Regular expression patterns are case insensitive by default.
+Regular expression patterns are case insensitive by default, see description
+of commands, which might override default behaviour.
|vifm-globs| section provides short overview of globs and some important points
that one needs to know about them.
diff --git a/data/vim/syntax/vifm.vim b/data/vim/syntax/vifm.vim
index 5a2236c3f..d15efd5aa 100644
--- a/data/vim/syntax/vifm.vim
+++ b/data/vim/syntax/vifm.vim
@@ -1,6 +1,6 @@
" vifm syntax file
" Maintainer: xaizek <xaizek@openmailbox.org>
-" Last Change: January 17, 2017
+" Last Change: May 14, 2017
" Based On: Vim syntax file by Dr. Charles E. Campbell, Jr.
if exists('b:current_syntax')
@@ -15,11 +15,11 @@ set cpo-=C
" General commands
syntax keyword vifmCommand contained alink apropos bmark bmarks bmgo change
\ chmod chown clone compare cope[n] co[py] cq[uit] d[elete] delbmarks
- \ delm[arks] di[splay] dirs e[dit] el[se] empty en[dif] exi[t] file filter
- \ fin[d] fini[sh] gr[ep] h[elp] his[tory] jobs locate ls lstrash marks
- \ mes[sages] mkdir m[ove] noh[lsearch] on[ly] popd pushd pu[t] pw[d] q[uit]
- \ redr[aw] reg[isters] rename restart restore rlink screen sh[ell] siblnext
- \ siblprev sor[t] sp[lit] s[ubstitute] touch tr trashes tree sync undol[ist]
+ \ delm[arks] di[splay] dirs e[dit] el[se] empty en[dif] exi[t] file fin[d]
+ \ fini[sh] gr[ep] h[elp] his[tory] jobs locate ls lstrash marks mes[sages]
+ \ mkdir m[ove] noh[lsearch] on[ly] popd pushd pu[t] pw[d] q[uit] redr[aw]
+ \ reg[isters] rename restart restore rlink screen sh[ell] siblnext siblprev
+ \ sor[t] sp[lit] s[ubstitute] touch tr trashes tree sync undol[ist]
\ ve[rsion] vie[w] vifm vs[plit] winc[md] w[rite] wq x[it] y[ank]
\ nextgroup=vifmArgs
@@ -51,7 +51,7 @@ syntax keyword vifmFtCommand contained filet[ype] filex[type] filev[iewer]
syntax keyword vifmExprCommand contained if ec[ho] elsei[f] exe[cute]
syntax keyword vifmNormalCommand contained norm[al]
\ nextgroup=vifmColonSubcommand
-syntax keyword vifmSelectCommands contained select unselect skipwhite
+syntax match vifmPatternCommands contained /\<\(filter\(!\|\>\)\|select\(!\|\>\)\|unselect\>\)/ skipwhite
\ nextgroup=vifmPattern
" List of event names for autocommands (case insensitive)
@@ -153,7 +153,7 @@ syntax region vifmStatement start='^\(\s\|:\)*'
\,vifmFtCommandSt,vifmCMapAbbr,vifmMap,vifmMapSt,vifmCMapSt,vifmExecute
\,vifmComment,vifmInlineComment,vifmNotComment,vifmExprCommandSt,vifmNormalCommandSt
\,vifmCdCommandSt,vifmSet,vifmArgument,vifmSoCommandSt,vifmPrefixCommands
- \,vifmAutocmdCommand,vifmAutoEvent,vifmSelectCommands
+ \,vifmAutocmdCommand,vifmAutoEvent,vifmPatternCommands
" Contained statement with highlighting of angle-brace notation.
syntax region vifmStatementCN start='\(\s\|:\)*'
\ skip='\(\n\s*\\\)\|\(\n\s*".*$\)' end='$' keepend contained
@@ -170,7 +170,7 @@ syntax region vifmStatementC start='\(\s\|:\)*'
\,vifmComment,vifmInlineComment,vifmNotComment,vifmExprCommandSt,vifmNormalCommandSt
\,vifmCdCommandSt,vifmSet,vifmArgument,vifmSoCommand,vifmSoCommandSt
\,vifmInvertCommand,vifmInvertCommandSt,vifmPrefixCommands
- \,vifmAutocmdCommand,vifmAutoEvent,vifmSelectCommands
+ \,vifmAutocmdCommand,vifmAutoEvent,vifmPatternCommands
syntax region vifmCmdCommandSt start='^\(\s\|:\)*com\%[mand]\>'
\ skip='\(\n\s*\\\)\|\(\n\s*".*$\)' end='$' keepend
\ contains=vifmCmdCommand,vifmComment,vifmInlineComment,vifmNotComment
@@ -386,7 +386,7 @@ syntax match vifmHiClear contained /\s*\<clear\>\s*/
" Highlight
highlight link vifmAutocmdCommand Statement
-highlight link vifmSelectCommands Statement
+highlight link vifmPatternCommands Statement
highlight link vifmComment Comment
highlight link vifmInlineComment Comment
highlight link vifmCommand Statement
diff --git a/src/cfg/info.c b/src/cfg/info.c
index 86cb998bf..d706c0c2b 100644
--- a/src/cfg/info.c
+++ b/src/cfg/info.c
@@ -39,6 +39,7 @@
#include "../utils/fs.h"
#include "../utils/log.h"
#include "../utils/macros.h"
+#include "../utils/matcher.h"
#include "../utils/matchers.h"
#include "../utils/path.h"
#include "../utils/str.h"
@@ -66,6 +67,7 @@ static void append_to_history(hist_t *hist, void (*saver)(const char[]),
static void ensure_history_not_full(hist_t *hist);
static void get_history(FileView *view, int reread, const char dir[],
const char file[], int rel_pos);
+static void set_manual_filter(FileView *view, const char value[]);
static void set_view_property(FileView *view, char type, const char value[]);
static int copy_file(const char src[], const char dst[]);
static int copy_file_internal(FILE *const src, FILE *const dst);
@@ -324,13 +326,11 @@ read_info_file(int reread)
}
else if(type == LINE_TYPE_LWIN_FILT)
{
- (void)replace_string(&lwin.prev_manual_filter, line_val);
- (void)filter_set(&lwin.manual_filter, line_val);
+ set_manual_filter(&lwin, line_val);
}
else if(type == LINE_TYPE_RWIN_FILT)
{
- (void)replace_string(&rwin.prev_manual_filter, line_val);
- (void)filter_set(&rwin.manual_filter, line_val);
+ set_manual_filter(&rwin, line_val);
}
else if(type == LINE_TYPE_LWIN_FILT_INV)
{
@@ -446,6 +446,30 @@ get_history(FileView *view, int reread, const char dir[], const char file[],
}
}
+/* Sets manual filter of the view and its previous state to given value. */
+static void
+set_manual_filter(FileView *view, const char value[])
+{
+ char *error;
+ matcher_t *matcher;
+
+ (void)replace_string(&view->prev_manual_filter, value);
+ matcher = matcher_alloc(value, FILTER_DEF_CASE_SENSITIVITY, 0, "", &error);
+ free(error);
+
+ /* If setting filter value has failed, try to setup an empty value instead. */
+ if(matcher == NULL)
+ {
+ (void)replace_string(&view->prev_manual_filter, "");
+ matcher = matcher_alloc("", FILTER_DEF_CASE_SENSITIVITY, 0, "", &error);
+ free(error);
+ assert(matcher != NULL && "Can't init manual filter.");
+ }
+
+ matcher_free(view->manual_filter);
+ view->manual_filter = matcher;
+}
+
/* Sets view property specified by the type to the value. */
static void
set_view_property(FileView *view, char type, const char value[])
@@ -1400,11 +1424,11 @@ static void
write_general_state(FILE *const fp)
{
fputs("\n# State:\n", fp);
- fprintf(fp, "f%s\n", lwin.manual_filter.raw);
+ fprintf(fp, "f%s\n", matcher_get_expr(lwin.manual_filter));
fprintf(fp, "i%d\n", lwin.invert);
fprintf(fp, "[.%d\n", lwin.hide_dot);
fprintf(fp, "[F%s\n", lwin.auto_filter.raw);
- fprintf(fp, "F%s\n", rwin.manual_filter.raw);
+ fprintf(fp, "F%s\n", matcher_get_expr(rwin.manual_filter));
fprintf(fp, "I%d\n", rwin.invert);
fprintf(fp, "].%d\n", rwin.hide_dot);
fprintf(fp, "]F%s\n", rwin.auto_filter.raw);
diff --git a/src/cmd_handlers.c b/src/cmd_handlers.c
index 708605bd8..526caff5d 100644
--- a/src/cmd_handlers.c
+++ b/src/cmd_handlers.c
@@ -72,6 +72,7 @@
#include "utils/filter.h"
#include "utils/fs.h"
#include "utils/log.h"
+#include "utils/matcher.h"
#include "utils/matchers.h"
#include "utils/path.h"
#include "utils/regexp.h"
@@ -173,10 +174,9 @@ static int filter_cmd(const cmd_info_t *cmd_info);
static int update_filter(FileView *view, const cmd_info_t *cmd_info);
static void display_filters_info(const FileView *view);
static char * get_filter_info(const char name[], const filter_t *filter);
-static int set_view_filter(FileView *view, const char filter[], int invert,
- int case_sensitive);
-static const char * get_filter_value(const char filter[]);
-static const char * try_compile_regex(const char regex[], int cflags);
+static char * get_matcher_info(const char name[], const matcher_t *matcher);
+static int set_view_filter(FileView *view, const char filter[],
+ const char fallback[], int invert);
static int get_filter_inversion_state(const cmd_info_t *cmd_info);
static int find_cmd(const cmd_info_t *cmd_info);
static int finish_cmd(const cmd_info_t *cmd_info);
@@ -493,8 +493,8 @@ const cmd_add_t cmds_list[] = {
.handler = &filextype_cmd, .min_args = 1, .max_args = NOT_DEF, },
{ .name = "filter", .abbr = NULL, .id = COM_FILTER,
.descr = "set/reset file filter",
- .flags = HAS_EMARK | HAS_QUOTED_ARGS | HAS_REGEXP_ARGS | HAS_QMARK_NO_ARGS,
- .handler = &filter_cmd, .min_args = 0, .max_args = 2, },
+ .flags = HAS_EMARK | HAS_REGEXP_ARGS | HAS_QMARK_NO_ARGS,
+ .handler = &filter_cmd, .min_args = 0, .max_args = NOT_DEF, },
{ .name = "find", .abbr = "fin", .id = COM_FIND,
.descr = "query find results",
.flags = HAS_RANGE | HAS_QUOTED_ARGS | HAS_MACROS_FOR_CMD
@@ -2145,6 +2145,8 @@ filter_cmd(const cmd_info_t *cmd_info)
static int
update_filter(FileView *view, const cmd_info_t *cmd_info)
{
+ const char *fallback = cfg_get_last_search_pattern();
+
if(cmd_info->argc == 0)
{
if(cmd_info->emark)
@@ -2152,31 +2154,14 @@ update_filter(FileView *view, const cmd_info_t *cmd_info)
toggle_filter_inversion(view);
return 0;
}
- else
- {
- const int invert_filter = get_filter_inversion_state(cmd_info);
- return set_view_filter(view, NULL, invert_filter,
- FILTER_DEF_CASE_SENSITIVITY) != 0;
- }
- }
- else
- {
- int invert_filter;
- int case_sensitive = FILTER_DEF_CASE_SENSITIVITY;
-
- if(cmd_info->argc == 2)
- {
- if(parse_case_flag(cmd_info->argv[1], &case_sensitive) != 0)
- {
- return CMDS_ERR_TRAILING_CHARS;
- }
- }
- invert_filter = get_filter_inversion_state(cmd_info);
-
- return set_view_filter(view, cmd_info->argv[0], invert_filter,
- case_sensitive) != 0;
+ /* When no arguments are provided, we don't want to fall back to last
+ * history entry. */
+ fallback = "";
}
+
+ return set_view_filter(view, cmd_info->args, fallback,
+ get_filter_inversion_state(cmd_info)) != 0;
}
/* Displays state of all filters on the status bar. */
@@ -2184,7 +2169,7 @@ static void
display_filters_info(const FileView *view)
{
char *const localf = get_filter_info("Local", &view->local_filter.filter);
- char *const manualf = get_filter_info("Name", &view->manual_filter);
+ char *const manualf = get_matcher_info("Name", view->manual_filter);
char *const autof = get_filter_info("Auto", &view->auto_filter);
status_bar_messagef("Filter -- Flags -- Value\n%s\n%s\n%s", localf, manualf,
@@ -2215,6 +2200,17 @@ get_filter_info(const char name[], const filter_t *filter)
return format_str("%-6s %-5s %s", name, flags_str, filter->raw);
}
+/* Composes description string for given matcher. Returns NULL on out of
+ * memory error, otherwise a newly allocated string, which should be freed by
+ * the caller, is returned. */
+static char *
+get_matcher_info(const char name[], const matcher_t *matcher)
+{
+ const char *const flags = matcher_is_empty(matcher) ? "" : "---->";
+ const char *const value = matcher_get_expr(matcher);
+ return format_str("%-6s %-5s %s", name, flags, value);
+}
+
/* Returns value for filter inversion basing on current configuration and
* filter command. */
static int
@@ -2229,74 +2225,30 @@ get_filter_inversion_state(const cmd_info_t *cmd_info)
}
/* Tries to update filter of the view rejecting incorrect regular expression.
- * NULL as filter value means filter reset, empty string means using of the last
- * search pattern. Returns non-zero if message on the statusbar should be
- * saved, otherwise zero is returned. */
+ * On empty pattern fallback is used. Returns non-zero if message on the
+ * statusbar should be saved, otherwise zero is returned. */
static int
-set_view_filter(FileView *view, const char filter[], int invert,
- int case_sensitive)
+set_view_filter(FileView *view, const char filter[], const char fallback[],
+ int invert)
{
- const char *error_msg;
-
- filter = get_filter_value(filter);
-
- error_msg = try_compile_regex(filter, REG_EXTENDED);
- if(error_msg != NULL)
+ char *error;
+ matcher_t *const matcher = matcher_alloc(filter, FILTER_DEF_CASE_SENSITIVITY,
+ 0, fallback, &error);
+ if(matcher == NULL)
{
- status_bar_errorf("Name filter not set: %s", error_msg);
+ status_bar_errorf("Name filter not set: %s", error);
+ free(error);
return 1;
}
view->invert = invert;
- (void)filter_change(&view->manual_filter, filter, case_sensitive);
+ matcher_free(view->manual_filter);
+ view->manual_filter = matcher;
(void)filter_clear(&view->auto_filter);
ui_view_schedule_reload(view);
return 0;
}
-/* Returns new value for a filter taking special values of the filter into
- * account. NULL means filter reset, empty string means using of the last
- * search pattern. Returns possibly changed filter value, which might be
- * invalidated after adding new search pattern/changing history size. */
-static const char *
-get_filter_value(const char filter[])
-{
- if(filter == NULL)
- {
- filter = "";
- }
- else if(filter[0] == '\0')
- {
- if(!hist_is_empty(&cfg.search_hist))
- {
- filter = cfg.search_hist.items[0];
- }
- }
- return filter;
-}
-
-/* Tries to compile given regular expression and specified compile flags.
- * Returns NULL on success, otherwise statically allocated message describing
- * compiling error is returned. */
-static const char *
-try_compile_regex(const char regex[], int cflags)
-{
- const char *error_msg = NULL;
-
- if(regex[0] != '\0')
- {
- regex_t re;
- const int err = regcomp(&re, regex, cflags);
- if(err != 0)
- {
- error_msg = get_regexp_error(err, &re);
- }
- regfree(&re);
- }
-
- return error_msg;
-}
-
static int
find_cmd(const cmd_info_t *cmd_info)
{
@@ -3639,7 +3591,7 @@ substitute_cmd(const cmd_info_t *cmd_info)
if(cmd_info->argc == 3)
{
/* TODO: maybe extract into a function to generalize code with
- * filter_cmd(). */
+ * parse_case_flag(). */
const char *flags = cmd_info->argv[2];
while(*flags != '\0')
{
@@ -3906,7 +3858,8 @@ sync_filters(void)
(void)filter_assign(&other_view->local_filter.filter,
&curr_view->local_filter.filter);
- (void)filter_assign(&other_view->manual_filter, &curr_view->manual_filter);
+ matcher_free(other_view->manual_filter);
+ other_view->manual_filter = matcher_clone(curr_view->manual_filter);
(void)filter_assign(&other_view->auto_filter, &curr_view->auto_filter);
ui_view_schedule_reload(other_view);
}
diff --git a/src/event_loop.c b/src/event_loop.c
index 451fc6d57..b486e5079 100644
--- a/src/event_loop.c
+++ b/src/event_loop.c
@@ -502,7 +502,7 @@ check_view_for_changes(FileView *view)
{
if(window_shows_dirlist(view))
{
- check_if_filelist_have_changed(view);
+ check_if_filelist_has_changed(view);
}
}
diff --git a/src/filelist.c b/src/filelist.c
index 9cdbbce7d..dbc505520 100644
--- a/src/filelist.c
+++ b/src/filelist.c
@@ -59,6 +59,7 @@
#include "utils/fswatch.h"
#include "utils/log.h"
#include "utils/macros.h"
+#include "utils/matcher.h"
#include "utils/path.h"
#include "utils/regexp.h"
#include "utils/str.h"
@@ -2309,7 +2310,7 @@ rescue_from_empty_filelist(FileView *view)
show_error_msgf("Filter error",
"The %s\"%s\" pattern did not match any files. It was reset.",
- view->invert ? "" : "inverted ", view->manual_filter.raw);
+ view->invert ? "" : "inverted ", matcher_get_expr(view->manual_filter));
filename_filter_clear(view);
@@ -2498,7 +2499,7 @@ alloc_dir_entry(dir_entry_t **list, int list_size)
}
void
-check_if_filelist_have_changed(FileView *view)
+check_if_filelist_has_changed(FileView *view)
{
int failed, changed;
const char *const curr_dir = flist_get_dir(view);
diff --git a/src/filelist.h b/src/filelist.h
index 2eefa7a77..9f51973c0 100644
--- a/src/filelist.h
+++ b/src/filelist.h
@@ -161,7 +161,7 @@ char * get_current_file_name(FileView *view);
dir_entry_t * get_current_entry(const FileView *view);
/* Checks whether content in the current directory of the view changed and
* reloads the view if so. */
-void check_if_filelist_have_changed(FileView *view);
+void check_if_filelist_has_changed(FileView *view);
/* Checks whether cd'ing into path is possible. Shows cd errors to a user.
* Returns non-zero if it's possible, zero otherwise. */
int cd_is_possible(const char *path);
diff --git a/src/filtering.c b/src/filtering.c
index b7597a2f2..fe0f76cb8 100644
--- a/src/filtering.c
+++ b/src/filtering.c
@@ -26,6 +26,7 @@
#include "compat/reallocarray.h"
#include "ui/ui.h"
#include "utils/dynarray.h"
+#include "utils/matcher.h"
#include "utils/path.h"
#include "utils/regexp.h"
#include "utils/str.h"
@@ -38,6 +39,7 @@
static void reset_filter(filter_t *filter);
static int is_newly_filtered(FileView *view, const dir_entry_t *entry,
void *arg);
+static void replace_matcher(matcher_t **matcher, const char expr[]);
static int file_is_filtered(FileView *view, const char filename[], int is_dir,
int apply_local_filter);
static int get_unfiltered_pos(const FileView *const view, int pos);
@@ -61,7 +63,7 @@ filters_view_reset(FileView *view)
view->prev_invert = view->invert;
(void)replace_string(&view->prev_manual_filter, "");
- reset_filter(&view->manual_filter);
+ replace_matcher(&view->manual_filter, "");
(void)replace_string(&view->prev_auto_filter, "");
reset_filter(&view->auto_filter);
@@ -220,7 +222,8 @@ remove_filename_filter(FileView *view)
return;
}
- (void)replace_string(&view->prev_manual_filter, view->manual_filter.raw);
+ (void)replace_string(&view->prev_manual_filter,
+ matcher_get_expr(view->manual_filter));
(void)replace_string(&view->prev_auto_filter, view->auto_filter.raw);
view->prev_invert = view->invert;
@@ -233,7 +236,7 @@ remove_filename_filter(FileView *view)
int
filename_filter_is_empty(FileView *view)
{
- return filter_is_empty(&view->manual_filter)
+ return matcher_is_empty(view->manual_filter)
&& filter_is_empty(&view->auto_filter);
}
@@ -241,7 +244,7 @@ void
filename_filter_clear(FileView *view)
{
filter_clear(&view->auto_filter);
- filter_clear(&view->manual_filter);
+ replace_matcher(&view->manual_filter, "");
view->invert = 1;
}
@@ -253,12 +256,25 @@ restore_filename_filter(FileView *view)
return;
}
- (void)filter_set(&view->manual_filter, view->prev_manual_filter);
+ replace_matcher(&view->manual_filter, view->prev_manual_filter);
+
(void)filter_set(&view->auto_filter, view->prev_auto_filter);
view->invert = view->prev_invert;
ui_view_schedule_reload(view);
}
+/* Changes *matcher to have the value of the expr. The operation is assumed to
+ * succeed, but it's not guaranteed. */
+static void
+replace_matcher(matcher_t **matcher, const char expr[])
+{
+ char *error;
+
+ matcher_free(*matcher);
+ *matcher = matcher_alloc(expr, FILTER_DEF_CASE_SENSITIVITY, 0, "", &error);
+ free(error);
+}
+
void
toggle_filter_inversion(FileView *view)
{
@@ -288,6 +304,8 @@ file_is_filtered(FileView *view, const char filename[], int is_dir,
{
/* FIXME: some very long file names won't be matched against some regexps. */
char name_with_slash[NAME_MAX + 1 + 1];
+ char path[PATH_MAX + sizeof(name_with_slash)];
+
if(is_dir)
{
append_slash(filename, name_with_slash, sizeof(name_with_slash));
@@ -305,19 +323,22 @@ file_is_filtered(FileView *view, const char filename[], int is_dir,
return 0;
}
- if(filter_is_empty(&view->manual_filter))
+ if(matcher_is_empty(view->manual_filter))
{
return 1;
}
- if(filter_matches(&view->manual_filter, filename) > 0)
+ if(matcher_is_full_path(view->manual_filter))
{
- return !view->invert;
- }
- else
- {
- return view->invert;
+ const size_t nchars = copy_str(path, sizeof(path) - 1, view->curr_dir);
+ path[nchars - 1U] = '/';
+ copy_str(path + nchars, sizeof(path) - nchars, filename);
+ filename = path;
}
+
+ return matcher_matches(view->manual_filter, filename)
+ ? !view->invert
+ : view->invert;
}
void
diff --git a/src/modes/cmdline.c b/src/modes/cmdline.c
index 76073ddd3..ceb2ed6b1 100644
--- a/src/modes/cmdline.c
+++ b/src/modes/cmdline.c
@@ -49,6 +49,7 @@
#include "../ui/statusline.h"
#include "../ui/ui.h"
#include "../utils/macros.h"
+#include "../utils/matcher.h"
#include "../utils/path.h"
#include "../utils/str.h"
#include "../utils/test_helpers.h"
@@ -1806,7 +1807,7 @@ cmd_ctrl_xxe(key_info_t key_info, keys_info_t *keys_info)
static void
cmd_ctrl_xm(key_info_t key_