xaizek / vifm (License: GPLv2+) (since 2018-12-07)
Vifm is a file manager with curses interface, which provides Vi[m]-like environment for managing objects within file systems, extended with some useful ideas from mutt.
Commit 9cc98729aab4557b956bd60128860b9703d6631c

Add display version of expr to utils/matchers
That is, separate value that's processed from its presentation. This
allows the clients to modify expressions if necessary while now leaking
that to the user. Not used for anything as of this commit.
Author: xaizek
Author date (UTC): 2026-07-21 12:22
Committer name: xaizek
Committer date (UTC): 2026-07-24 17:32
Parent(s): 035d8e16b011dbd22a326ef0e15a0feea4874c13
Signing key: 99DC5E4DB05F6BE2
Tree: f9022aa44cd3578a1bf0830ee85b1969560aab55
File Lines added Lines deleted
src/cmd_handlers.c 1 1
src/filetype.c 1 1
src/flist_sel.c 2 2
src/opt_handlers.c 1 1
src/utils/matchers.c 3 3
src/utils/matchers.h 10 5
tests/fuzz/suite.c 4 4
tests/utils/matchers.c 22 12
File src/cmd_handlers.c changed (mode: 100644) (index f6fff160a..0acf4a26b)
... ... highlight_file(const cmd_info_t *cmd_info)
2962 2962
2963 2963 (void)extract_part(cmd_info->args, " \t", pattern); (void)extract_part(cmd_info->args, " \t", pattern);
2964 2964
2965 matchers = matchers_alloc(pattern, 0, 1, "", &error);
2965 matchers = matchers_alloc(pattern, pattern, 0, 1, "", &error);
2966 2966 if(matchers == NULL) if(matchers == NULL)
2967 2967 { {
2968 2968 ui_sb_errf("Pattern error: %s", error); ui_sb_errf("Pattern error: %s", error);
File src/filetype.c changed (mode: 100644) (index b6cfc51c6..ff7c5b9dd)
... ... ft_mg_from_string(const char str[], matchers_group_t *mg, char **error)
1055 1055 for(i = 0; i < nexprs; ++i, ++result.count) for(i = 0; i < nexprs; ++i, ++result.count)
1056 1056 { {
1057 1057 char *matcher_error; char *matcher_error;
1058 result.list[i] = matchers_alloc(exprs[i], /*cs_by_def=*/0,
1058 result.list[i] = matchers_alloc(exprs[i], exprs[i], /*cs_by_def=*/0,
1059 1059 /*glob_by_def=*/1, /*on_empty_re=*/"", &matcher_error); /*glob_by_def=*/1, /*on_empty_re=*/"", &matcher_error);
1060 1060 if(result.list[i] == NULL) if(result.list[i] == NULL)
1061 1061 { {
File src/flist_sel.c changed (mode: 100644) (index 812af3d75..39a9532ac)
... ... flist_sel_by_pattern(view_t *view, const char pattern[], int erase_old,
354 354 { {
355 355 int i; int i;
356 356 char *error; char *error;
357 matchers_t *const ms = matchers_alloc(pattern, 0, 1, hists_search_last(),
358 &error);
357 matchers_t *const ms =
358 matchers_alloc(pattern, pattern, 0, 1, hists_search_last(), &error);
359 359 if(ms == NULL) if(ms == NULL)
360 360 { {
361 361 ui_sb_errf("Pattern error: %s", error); ui_sb_errf("Pattern error: %s", error);
File src/opt_handlers.c changed (mode: 100644) (index dbf27943e..76cefebaa)
... ... str_to_classify(const char str[], char type_decs[FT_COUNT][2][9])
1929 1929 else if(expr != NULL) else if(expr != NULL)
1930 1930 { {
1931 1931 char *error; char *error;
1932 matchers_t *const ms = matchers_alloc(expr, 0, 1, "", &error);
1932 matchers_t *const ms = matchers_alloc(expr, expr, 0, 1, "", &error);
1933 1933 if(ms == NULL) if(ms == NULL)
1934 1934 { {
1935 1935 vle_tb_append_linef(vle_err, "Wrong pattern (%s): %s", expr, error); vle_tb_append_linef(vle_err, "Wrong pattern (%s): %s", expr, error);
File src/utils/matchers.c changed (mode: 100644) (index fd75f2a90..6aad77a43)
... ... static void load_token(parsing_state_t *state, int single_char);
78 78 static int get_token_width(TokenType tok); static int get_token_width(TokenType tok);
79 79
80 80 matchers_t * matchers_t *
81 matchers_alloc(const char expr[], int cs_by_def, int glob_by_def,
82 const char on_empty_re[], char **error)
81 matchers_alloc(const char expr[], const char list[], int cs_by_def,
82 int glob_by_def, const char on_empty_re[], char **error)
83 83 { {
84 84 int nsubs; int nsubs;
85 char **subs = break_into_matchers(expr, &nsubs, /*is_list=*/0);
85 char **subs = break_into_matchers(list, &nsubs, /*is_list=*/0);
86 86
87 87 MatcherExpr expr_kind = (glob_by_def ? ME_DEF_GLOB : ME_DEF_REGEX); MatcherExpr expr_kind = (glob_by_def ? ME_DEF_GLOB : ME_DEF_REGEX);
88 88 matchers_t *matchers = matchers_init(expr, subs, nsubs, cs_by_def, matchers_t *matchers = matchers_init(expr, subs, nsubs, cs_by_def,
File src/utils/matchers.h changed (mode: 100644) (index 2baf31d8c..32f8e521e)
24 24 /* Opaque matchers type. */ /* Opaque matchers type. */
25 25 typedef struct matchers_t matchers_t; typedef struct matchers_t matchers_t;
26 26
27 /* Arguments and return value match matcher_alloc() except for first argument,
28 * which is a list here. */
29 matchers_t * matchers_alloc(const char list[], int cs_by_def, int glob_by_def,
30 const char on_empty_re[], char **error);
27 /* Parses a conjunction (AND) of matcher expressions and allocates matchers.
28 * The expr parameter is what matchers_is_expr() will return, the list parameter
29 * is what gets parsed. on_empty_re string is used if passed in regexp is
30 * empty. Returns a new instance on success and sets *error to NULL, otherwise
31 * NULL is returned and *error is initialized with a newly allocated string
32 * describing the error. */
33 matchers_t * matchers_alloc(const char expr[], const char list[], int cs_by_def,
34 int glob_by_def, const char on_empty_re[], char **error);
31 35
32 36 /* Makes a copy of existing matchers. Returns the clone, or NULL on error. */ /* Makes a copy of existing matchers. Returns the clone, or NULL on error. */
33 37 matchers_t * matchers_clone(const matchers_t *matchers); matchers_t * matchers_clone(const matchers_t *matchers);
 
... ... int matchers_match(const matchers_t *matchers, const char path[]);
43 47 * directories. Returns non-zero if so, otherwise zero is returned. */ * directories. Returns non-zero if so, otherwise zero is returned. */
44 48 int matchers_match_dir(const matchers_t *matchers, const char path[]); int matchers_match_dir(const matchers_t *matchers, const char path[]);
45 49
46 /* Retrieves original matcher expression. Returns the expression. */
50 /* Retrieves original matcher expression (may not exactly match the value, see
51 * matchers_alloc()). Returns the expression. */
47 52 const char * matchers_get_expr(const matchers_t *matchers); const char * matchers_get_expr(const matchers_t *matchers);
48 53
49 54 /* Checks whether matchers matches at least superset of what like is matching. /* Checks whether matchers matches at least superset of what like is matching.
File tests/fuzz/suite.c changed (mode: 100644) (index a70996824..8d2995950)
... ... fuzz_matchers(const char input[])
255 255
256 256 matchers_t *ms; matchers_t *ms;
257 257
258 ms = matchers_alloc(input, 0, 0, "", &error);
258 ms = matchers_alloc(input, input, 0, 0, "", &error);
259 259 free(error); free(error);
260 260 matchers_free(ms); matchers_free(ms);
261 261
262 ms = matchers_alloc(input, 0, 1, "", &error);
262 ms = matchers_alloc(input, input, 0, 1, "", &error);
263 263 free(error); free(error);
264 264 matchers_free(ms); matchers_free(ms);
265 265
266 ms = matchers_alloc(input, 1, 0, "", &error);
266 ms = matchers_alloc(input, input, 1, 0, "", &error);
267 267 free(error); free(error);
268 268 matchers_free(ms); matchers_free(ms);
269 269
270 ms = matchers_alloc(input, 1, 1, "", &error);
270 ms = matchers_alloc(input, input, 1, 1, "", &error);
271 271 free(error); free(error);
272 272 matchers_free(ms); matchers_free(ms);
273 273
File tests/utils/matchers.c changed (mode: 100644) (index 8d886da43..3eb9d6a20)
6 6 #include "../../src/utils/matchers.h" #include "../../src/utils/matchers.h"
7 7 #include "../../src/utils/string_array.h" #include "../../src/utils/string_array.h"
8 8
9 static matchers_t * make_matchers(const char expr[], char **error);
10
9 11 TEST(freeing_null_matchers_does_nothing) TEST(freeing_null_matchers_does_nothing)
10 12 { {
11 13 matchers_free(NULL); matchers_free(NULL);
 
... ... TEST(empty_patterns_are_disallowed)
23 25 TEST(wrong_expr_produces_error) TEST(wrong_expr_produces_error)
24 26 { {
25 27 char *error = NULL; char *error = NULL;
26 assert_null(matchers_alloc("/*/{?}", 0, 1, "", &error));
28 assert_null(make_matchers("/*/{?}", &error));
27 29 assert_non_null(error); assert_non_null(error);
28 30 free(error); free(error);
29 31 } }
 
... ... TEST(wrong_expr_produces_error)
31 33 TEST(none_matchers_match) TEST(none_matchers_match)
32 34 { {
33 35 char *error = NULL; char *error = NULL;
34 matchers_t *const ms = matchers_alloc("{[a]}{?}", 0, 1, "", &error);
36 matchers_t *const ms = make_matchers("{[a]}{?}", &error);
35 37 assert_string_equal(NULL, error); assert_string_equal(NULL, error);
36 38 assert_false(matchers_match(ms, "xx")); assert_false(matchers_match(ms, "xx"));
37 39 matchers_free(ms); matchers_free(ms);
 
... ... TEST(none_matchers_match)
40 42 TEST(only_first_matches) TEST(only_first_matches)
41 43 { {
42 44 char *error = NULL; char *error = NULL;
43 matchers_t *const ms = matchers_alloc("{ab}{?}", 0, 1, "", &error);
45 matchers_t *const ms = make_matchers("{ab}{?}", &error);
44 46 assert_string_equal(NULL, error); assert_string_equal(NULL, error);
45 47 assert_false(matchers_match(ms, "ab")); assert_false(matchers_match(ms, "ab"));
46 48 matchers_free(ms); matchers_free(ms);
 
... ... TEST(only_first_matches)
49 51 TEST(only_second_matches) TEST(only_second_matches)
50 52 { {
51 53 char *error = NULL; char *error = NULL;
52 matchers_t *const ms = matchers_alloc("{ab}{?}", 0, 1, "", &error);
54 matchers_t *const ms = make_matchers("{ab}{?}", &error);
53 55 assert_string_equal(NULL, error); assert_string_equal(NULL, error);
54 56 assert_false(matchers_match(ms, "a")); assert_false(matchers_match(ms, "a"));
55 57 matchers_free(ms); matchers_free(ms);
 
... ... TEST(only_second_matches)
58 60 TEST(both_matchers_match) TEST(both_matchers_match)
59 61 { {
60 62 char *error = NULL; char *error = NULL;
61 matchers_t *const ms = matchers_alloc("{[a]}{?}", 0, 1, "", &error);
63 matchers_t *const ms = make_matchers("{[a]}{?}", &error);
62 64 assert_string_equal(NULL, error); assert_string_equal(NULL, error);
63 65 assert_true(matchers_match(ms, "a")); assert_true(matchers_match(ms, "a"));
64 66 matchers_free(ms); matchers_free(ms);
65 67 } }
66 68
67 TEST(get_expr_returns_original_expr)
69 TEST(get_expr_returns_expr)
68 70 { {
69 71 char *error = NULL; char *error = NULL;
70 matchers_t *const ms = matchers_alloc("{[a]}{?}", 0, 1, "", &error);
72 matchers_t *const ms = matchers_alloc("pretty", "{[a]}{?}", /*cs_by_def=*/0,
73 /*glob_by_def=*/1, /*on_empty_re=*/"", &error);
74
71 75 assert_string_equal(NULL, error); assert_string_equal(NULL, error);
72 assert_string_equal("{[a]}{?}", matchers_get_expr(ms));
76 assert_string_equal("pretty", matchers_get_expr(ms));
73 77 matchers_free(ms); matchers_free(ms);
74 78 } }
75 79
 
... ... TEST(matchers_are_cloned)
211 215 char *error = NULL; char *error = NULL;
212 216 matchers_t *ms, *clone; matchers_t *ms, *clone;
213 217
214 assert_non_null(ms = matchers_alloc("{*.ext}/.*/", 0, 1, "", &error));
218 assert_non_null(ms = make_matchers("{*.ext}/.*/", &error));
215 219 assert_null(error); assert_null(error);
216 220
217 221 assert_non_null(clone = matchers_clone(ms)); assert_non_null(clone = matchers_clone(ms));
 
... ... TEST(inclusion_check_works)
230 234 char *error = NULL; char *error = NULL;
231 235 matchers_t *ms1, *ms2; matchers_t *ms1, *ms2;
232 236
233 assert_non_null(
234 ms1 = matchers_alloc("<*.cpp,*.c><*.cpp,*.d>", 0, 1, "", &error));
237 assert_non_null(ms1 = make_matchers("<*.cpp,*.c><*.cpp,*.d>", &error));
235 238 assert_null(error); assert_null(error);
236 assert_non_null(ms2 = matchers_alloc("<*.c><*.cpp>", 0, 1, "", &error));
239 assert_non_null(ms2 = make_matchers("<*.c><*.cpp>", &error));
237 240 assert_null(error); assert_null(error);
238 241
239 242 assert_true(matchers_includes(ms1, ms1)); assert_true(matchers_includes(ms1, ms1));
 
... ... TEST(breaking_list_of_lists)
259 262 free_string_array(matchers, nmatchers); free_string_array(matchers, nmatchers);
260 263 } }
261 264
265 static matchers_t *
266 make_matchers(const char expr[], char **error)
267 {
268 return matchers_alloc(expr, expr, /*cs_by_def=*/0, /*glob_by_def=*/1,
269 /*on_empty_re=*/"", error);
270 }
271
262 272 /* vim: set tabstop=2 softtabstop=2 shiftwidth=2 noexpandtab cinoptions-=(0 : */ /* vim: set tabstop=2 softtabstop=2 shiftwidth=2 noexpandtab cinoptions-=(0 : */
263 273 /* vim: set cinoptions+=t0 filetype=c : */ /* vim: set cinoptions+=t0 filetype=c : */
Hints

Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://code.reversed.top/user/xaizek/vifm

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@code.reversed.top/user/xaizek/vifm

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a pull request:
... clone the repository ...
... make some changes and some commits ...
git push origin master