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 e40063483ee87e083cb684198a8ac590169054f1

Keep file matchers grouped together
Previously, each matcher was registered separately to the same list of
programs/viewers. Now a single record associating matchers group to the
same list of programs/viewers is made.
Author: xaizek
Author date (UTC): 2025-07-14 07:26
Committer name: xaizek
Committer date (UTC): 2025-07-14 07:26
Parent(s): f8b6633bf9566756c6a0caf17bb16b158569b3d9
Signing key: 99DC5E4DB05F6BE2
Tree: 848d6c917621522b599e4f9e82fd1ea30ad63c00
File Lines added Lines deleted
src/cfg/info.c 26 14
src/cmd_handlers.c 17 26
src/filetype.c 155 20
src/filetype.h 28 10
tests/filetype/test.c 8 11
tests/menus/filetypes.c 12 10
tests/misc/quickview.c 15 16
tests/misc/running.c 55 45
tests/misc/view_mode.c 10 7
tests/misc/vifminfo.c 9 9
File src/cfg/info.c changed (mode: 100644) (index 771ae2813..4b12c1096)
47 47 #include "../utils/log.h" #include "../utils/log.h"
48 48 #include "../utils/macros.h" #include "../utils/macros.h"
49 49 #include "../utils/matcher.h" #include "../utils/matcher.h"
50 #include "../utils/matchers.h"
51 50 #include "../utils/parson.h" #include "../utils/parson.h"
52 51 #include "../utils/path.h" #include "../utils/path.h"
53 52 #include "../utils/str.h" #include "../utils/str.h"
 
... ... load_assocs(JSON_Object *root, const char node[], int for_x)
985 984 if(get_str(entry, "matchers", &matchers) && get_str(entry, "cmd", &cmd)) if(get_str(entry, "matchers", &matchers) && get_str(entry, "cmd", &cmd))
986 985 { {
987 986 char *error; char *error;
988 matchers_t *const ms = matchers_alloc(matchers, 0, 1, "", &error);
989 if(ms == NULL)
987 matchers_group_t mg;
988 if(ft_mg_from_string(matchers, &mg, &error) != 0)
990 989 { {
991 LOG_ERROR_MSG("Error with matchers of an assoc `%s`: %s", matchers,
992 error);
993 free(error);
990 if(error != NULL)
991 {
992 LOG_ERROR_MSG("Error with matchers of an assoc `%s`: %s", matchers,
993 error);
994 free(error);
995 }
994 996 } }
995 997 else else
996 998 { {
997 ft_set_programs(ms, cmd, for_x, in_x);
999 ft_set_programs(mg, cmd, for_x, in_x);
998 1000 } }
999 1001 } }
1000 1002 } }
 
... ... load_viewers(JSON_Object *root)
1015 1017 if(get_str(viewer, "matchers", &matchers) && get_str(viewer, "cmd", &cmd)) if(get_str(viewer, "matchers", &matchers) && get_str(viewer, "cmd", &cmd))
1016 1018 { {
1017 1019 char *error; char *error;
1018 matchers_t *const ms = matchers_alloc(matchers, 0, 1, "", &error);
1019 if(ms == NULL)
1020 matchers_group_t mg;
1021 if(ft_mg_from_string(matchers, &mg, &error) != 0)
1020 1022 { {
1021 LOG_ERROR_MSG("Error with matchers of a viewer `%s`: %s", matchers,
1022 error);
1023 free(error);
1023 if(error != NULL)
1024 {
1025 LOG_ERROR_MSG("Error with matchers of a viewer `%s`: %s", matchers,
1026 error);
1027 free(error);
1028 }
1024 1029 } }
1025 1030 else else
1026 1031 { {
1027 ft_set_viewers(ms, cmd);
1032 ft_set_viewers(mg, cmd);
1028 1033 } }
1029 1034 } }
1030 1035 } }
 
... ... store_assocs(JSON_Object *root, const char node[], assoc_list_t *assocs)
2421 2426 char *doubled_commas_cmd = double_char(ft_record.command, ','); char *doubled_commas_cmd = double_char(ft_record.command, ',');
2422 2427
2423 2428 JSON_Object *entry = append_object(entries); JSON_Object *entry = append_object(entries);
2424 set_str(entry, "matchers", matchers_get_expr(assoc.matchers));
2429
2430 /* Just make an incomplete object on error, parsing will discard it. */
2431 char *mg_str = ft_mg_to_string(&assoc.mg);
2432 if(mg_str != NULL)
2433 {
2434 set_str(entry, "matchers", mg_str);
2435 free(mg_str);
2436 }
2425 2437
2426 2438 if(ft_record.description[0] == '\0') if(ft_record.description[0] == '\0')
2427 2439 { {
File src/cmd_handlers.c changed (mode: 100644) (index 7751a99fd..323cff52c)
... ... fileviewer_cmd(const cmd_info_t *cmd_info)
2503 2503 static int static int
2504 2504 add_assoc(const cmd_info_t *cmd_info, int viewer, int for_x) add_assoc(const cmd_info_t *cmd_info, int viewer, int for_x)
2505 2505 { {
2506 char **matchers;
2507 int nmatchers;
2508 int i;
2509 2506 const int in_x = (curr_stats.exec_env_type == EET_EMULATOR_WITH_X); const int in_x = (curr_stats.exec_env_type == EET_EMULATOR_WITH_X);
2510 2507 const char *const records = vle_cmds_next_arg(cmd_info->args); const char *const records = vle_cmds_next_arg(cmd_info->args);
2511 2508
 
... ... add_assoc(const cmd_info_t *cmd_info, int viewer, int for_x)
2516 2513 : (show_fileprograms_menu(curr_view, cmd_info->argv[0]) != 0); : (show_fileprograms_menu(curr_view, cmd_info->argv[0]) != 0);
2517 2514 } }
2518 2515
2519 matchers = matchers_list(cmd_info->argv[0], &nmatchers);
2520 if(matchers == NULL)
2521 {
2522 return CMDS_ERR_NO_MEM;
2523 }
2524
2525 for(i = 0; i < nmatchers; ++i)
2516 char *error;
2517 matchers_group_t mg;
2518 if(ft_mg_from_string(cmd_info->argv[0], &mg, &error) != 0)
2526 2519 { {
2527 char *error;
2528 matchers_t *const ms = matchers_alloc(matchers[i], 0, 1, "", &error);
2529 if(ms == NULL)
2520 if(error == NULL)
2530 2521 { {
2531 ui_sb_errf("Wrong pattern (%s): %s", matchers[i], error);
2532 free(error);
2533 free_string_array(matchers, nmatchers);
2534 return CMDS_ERR_CUSTOM;
2522 return CMDS_ERR_NO_MEM;
2535 2523 } }
2536 2524
2537 if(viewer)
2538 {
2539 ft_set_viewers(ms, records);
2540 }
2541 else
2542 {
2543 ft_set_programs(ms, records, for_x, in_x);
2544 }
2525 ui_sb_err(error);
2526 free(error);
2527 return CMDS_ERR_CUSTOM;
2528 }
2529
2530 if(viewer)
2531 {
2532 ft_set_viewers(mg, records);
2533 }
2534 else
2535 {
2536 ft_set_programs(mg, records, for_x, in_x);
2545 2537 } }
2546 2538
2547 free_string_array(matchers, nmatchers);
2548 2539 return 0; return 0;
2549 2540 } }
2550 2541
File src/filetype.c changed (mode: 100644) (index 968ffd009..36691aac8)
... ... static int assoc_records_contains(assoc_records_t *assocs, const char command[],
53 53 const char description[]); const char description[]);
54 54 static void safe_free(char **adr); static void safe_free(char **adr);
55 55 static int is_assoc_record_empty(const assoc_record_t *record); static int is_assoc_record_empty(const assoc_record_t *record);
56 static int mg_match(const matchers_group_t *mg, const char str[]);
57 static void mg_free(matchers_group_t *mg);
56 58
57 59 const assoc_record_t NONE_PSEUDO_PROG = { const assoc_record_t NONE_PSEUDO_PROG = {
58 60 .command = "", .command = "",
 
... ... ft_get_viewers(const char file[])
116 118 { {
117 119 assoc_t *const assoc = &fileviewers.list[i]; assoc_t *const assoc = &fileviewers.list[i];
118 120
119 if(!matchers_match(assoc->matchers, file))
121 if(!mg_match(&assoc->mg, file))
120 122 { {
121 123 continue; continue;
122 124 } }
 
... ... find_existing_cmd(const assoc_list_t *record_list, const char file[])
149 151 assoc_record_t prog; assoc_record_t prog;
150 152 assoc_t *const assoc = &record_list->list[i]; assoc_t *const assoc = &record_list->list[i];
151 153
152 if(!matchers_match(assoc->matchers, file))
154 if(!mg_match(&assoc->mg, file))
153 155 { {
154 156 continue; continue;
155 157 } }
 
... ... ft_get_all_programs(const char file[])
190 192 } }
191 193
192 194 void void
193 ft_set_programs(matchers_t *matchers, const char programs[], int for_x,
194 int in_x)
195 ft_set_programs(matchers_group_t mg, const char programs[], int for_x, int in_x)
195 196 { {
196 197 const assoc_t assoc = { const assoc_t assoc = {
197 .matchers = matchers,
198 .mg = mg,
198 199 .records = parse_command_list(programs, 1), .records = parse_command_list(programs, 1),
199 200 }; };
200 201
201 202 /* On error, add_assoc() frees assoc, so just exit then. */ /* On error, add_assoc() frees assoc, so just exit then. */
202 if(add_assoc(for_x ? &xfiletypes : &filetypes, assoc) == 0)
203 if(add_assoc(for_x ? &xfiletypes : &filetypes, assoc))
203 204 { {
204 205 if(!for_x || in_x) if(!for_x || in_x)
205 206 { {
 
... ... clone_all_matching_records(const char file[], const assoc_list_t *record_list)
261 262 for(i = 0; i < record_list->count; ++i) for(i = 0; i < record_list->count; ++i)
262 263 { {
263 264 assoc_t *const assoc = &record_list->list[i]; assoc_t *const assoc = &record_list->list[i];
264 if(matchers_match(assoc->matchers, file))
265 if(mg_match(&assoc->mg, file))
265 266 { {
266 267 ft_assoc_record_add_all(&result, &assoc->records); ft_assoc_record_add_all(&result, &assoc->records);
267 268 } }
 
... ... clone_all_matching_records(const char file[], const assoc_list_t *record_list)
271 272 } }
272 273
273 274 void void
274 ft_set_viewers(matchers_t *matchers, const char viewers[])
275 ft_set_viewers(matchers_group_t mg, const char viewers[])
275 276 { {
276 277 const assoc_t assoc = { const assoc_t assoc = {
277 .matchers = matchers,
278 .mg = mg,
278 279 .records = parse_command_list(viewers, 0), .records = parse_command_list(viewers, 0),
279 280 }; };
280 281 /* On error, add_assoc() frees assoc, so just exit then. */ /* On error, add_assoc() frees assoc, so just exit then. */
 
... ... ft_set_viewers(matchers_t *matchers, const char viewers[])
282 283 } }
283 284
284 285 /* Adds association to the list of associations, takes ownership of it /* Adds association to the list of associations, takes ownership of it
285 * regardless of the outcome. Returns non-zero on out of memory error,
286 * otherwise zero is returned. */
286 * regardless of the outcome. Returns non-zero if the element was added,
287 * otherwise it's freed. */
287 288 static int static int
288 289 add_assoc(assoc_list_t *assoc_list, assoc_t assoc) add_assoc(assoc_list_t *assoc_list, assoc_t assoc)
289 290 { {
 
... ... add_assoc(assoc_list_t *assoc_list, assoc_t assoc)
304 305 { {
305 306 free_assoc(&assoc); free_assoc(&assoc);
306 307 show_error_msg("Memory Error", "Unable to allocate enough memory"); show_error_msg("Memory Error", "Unable to allocate enough memory");
307 return 1;
308 return 0;
308 309 } }
309 310
310 311 assoc_list->list = p; assoc_list->list = p;
311 312 assoc_list->list[assoc_list->count] = assoc; assoc_list->list[assoc_list->count] = assoc;
312 313 assoc_list->count++; assoc_list->count++;
313 return 0;
314 return 1;
314 315 } }
315 316
316 317 /* Compares two associations for equality. Returns non-zero if they are equal, /* Compares two associations for equality. Returns non-zero if they are equal,
 
... ... is_assoc_equal(const assoc_t *a, const assoc_t *b)
323 324 return 0; return 0;
324 325 } }
325 326
326 if(strcmp(matchers_get_expr(a->matchers),
327 matchers_get_expr(b->matchers)) != 0)
327 if(a->mg.count != b->mg.count)
328 328 { {
329 329 return 0; return 0;
330 330 } }
331 331
332 332 int i; int i;
333
334 /* In principle, we're only interested whether we have the same set of
335 * matchers or not regardless of their order, but since the goal is to avoid
336 * essentially identical duplicates, just do pair-wise comparison. */
337 for(i = 0; i < a->mg.count; ++i)
338 {
339 if(strcmp(matchers_get_expr(a->mg.list[i]),
340 matchers_get_expr(b->mg.list[i])) != 0)
341 {
342 return 0;
343 }
344 }
345
333 346 for(i = 0; i < a->records.count; ++i) for(i = 0; i < a->records.count; ++i)
334 347 { {
335 348 if(strcmp(a->records.list[i].command, b->records.list[i].command) != 0) if(strcmp(a->records.list[i].command, b->records.list[i].command) != 0)
 
... ... is_assoc_equal(const assoc_t *a, const assoc_t *b)
337 350 return 0; return 0;
338 351 } }
339 352 } }
353
340 354 return 1; return 1;
341 355 } }
342 356
 
... ... static void
389 403 add_defaults(int in_x) add_defaults(int in_x)
390 404 { {
391 405 char *error; char *error;
392 matchers_t *const m = matchers_alloc("{*/}", 0, 1, "", &error);
393 assert(m != NULL && "Failed to allocate builtin matcher!");
406 matchers_group_t mg;
407 if(ft_mg_from_string("{*/}", &mg, &error) != 0)
408 {
409 assert(0 && "Failed to allocate builtin matcher!");
410 }
394 411
395 412 new_records_type = ART_BUILTIN; new_records_type = ART_BUILTIN;
396 ft_set_programs(m, "{Enter directory}" VIFM_PSEUDO_CMD, 0, in_x);
413 ft_set_programs(mg, "{Enter directory}" VIFM_PSEUDO_CMD, /*for_x=*/0, in_x);
397 414 new_records_type = ART_CUSTOM; new_records_type = ART_CUSTOM;
398 415 } }
399 416
 
... ... reset_list_head(assoc_list_t *assoc_list)
419 436 static void static void
420 437 free_assoc(assoc_t *assoc) free_assoc(assoc_t *assoc)
421 438 { {
422 matchers_free(assoc->matchers);
439 mg_free(&assoc->mg);
423 440 ft_assoc_records_free(&assoc->records); ft_assoc_records_free(&assoc->records);
424 441 } }
425 442
 
... ... ft_assoc_exists(const assoc_list_t *assocs, const char pattern[],
472 489 int j; int j;
473 490
474 491 const assoc_t assoc = assocs->list[i]; const assoc_t assoc = assocs->list[i];
475 if(strcmp(matchers_get_expr(assoc.matchers), pattern) != 0)
492
493 char *mg_str = ft_mg_to_string(&assoc.mg);
494 if(mg_str == NULL)
495 {
496 show_error_msg("Memory Error", "Unable to allocate enough memory");
497 return 0;
498 }
499
500 if(strcmp(mg_str, pattern) != 0)
476 501 { {
502 free(mg_str);
477 503 continue; continue;
478 504 } }
505 free(mg_str);
479 506
480 507 for(j = 0; j < assoc.records.count; ++j) for(j = 0; j < assoc.records.count; ++j)
481 508 { {
 
... ... is_assoc_record_empty(const assoc_record_t *record)
607 634 return record->command == NULL && record->description == NULL; return record->command == NULL && record->description == NULL;
608 635 } }
609 636
637 /* Checks whether given string matches. Returns non-zero if so, otherwise zero
638 * is returned. */
639 static int
640 mg_match(const matchers_group_t *mg, const char str[])
641 {
642 int i;
643 for(i = 0; i < mg->count; ++i)
644 {
645 if(matchers_match(mg->list[i], str))
646 {
647 return 1;
648 }
649 }
650 return 0;
651 }
652
653 int
654 ft_mg_from_string(const char str[], matchers_group_t *mg, char **error)
655 {
656 *error = NULL;
657
658 int nexprs;
659 char **exprs = matchers_list(str, &nexprs);
660 if(exprs == NULL)
661 {
662 update_string(error, "Failed to parse list of matchers.");
663 return 1;
664 }
665
666 matchers_group_t result = {
667 .list = reallocarray(NULL, nexprs, sizeof(*result.list)),
668 .count = 0,
669 };
670 if(result.list == NULL)
671 {
672 free_string_array(exprs, nexprs);
673 update_string(error, "Failed to allocate matchers.");
674 return 1;
675 }
676
677 int i;
678 for(i = 0; i < nexprs; ++i, ++result.count)
679 {
680 char *matcher_error;
681 result.list[i] = matchers_alloc(exprs[i], /*cs_by_def=*/0,
682 /*glob_by_def=*/1, /*on_empty_re=*/"", &matcher_error);
683 if(result.list[i] == NULL)
684 {
685 put_string(error, format_str("Wrong pattern (%s): %s", exprs[i],
686 matcher_error));
687 free(matcher_error);
688 break;
689 }
690 }
691
692 free_string_array(exprs, nexprs);
693
694 if(i < nexprs)
695 {
696 mg_free(&result);
697 return 1;
698 }
699
700 *mg = result;
701 return 0;
702 }
703
704 /* Frees matchers in a group of matchers. */
705 static void
706 mg_free(matchers_group_t *mg)
707 {
708 int i;
709 for(i = 0; i < mg->count; ++i)
710 {
711 matchers_free(mg->list[i]);
712 }
713 free(mg->list);
714 }
715
716 char *
717 ft_mg_to_string(const matchers_group_t *mg)
718 {
719 char *str = NULL;
720 size_t len = 0;
721
722 int i;
723 for(i = 0; i < mg->count; ++i)
724 {
725 if(i != 0 && strappendch(&str, &len, ',') != 0)
726 {
727 break;
728 }
729
730 if(strappend(&str, &len, matchers_get_expr(mg->list[i])) != 0)
731 {
732 break;
733 }
734 }
735
736 if(i < mg->count)
737 {
738 free(str);
739 return NULL;
740 }
741
742 return str;
743 }
744
610 745 /* vim: set tabstop=2 softtabstop=2 shiftwidth=2 noexpandtab cinoptions-=(0 : */ /* vim: set tabstop=2 softtabstop=2 shiftwidth=2 noexpandtab cinoptions-=(0 : */
611 746 /* vim: set cinoptions+=t0 filetype=c : */ /* vim: set cinoptions+=t0 filetype=c : */
File src/filetype.h changed (mode: 100644) (index be83a604f..ab542ee04)
... ... typedef struct
58 58 } }
59 59 assoc_records_t; assoc_records_t;
60 60
61 /* List of matchers which matches if any matcher does. */
62 typedef struct
63 {
64 struct matchers_t **list; /* Disjunction of matching mechanisms. */
65 int count; /* Size of the list. */
66 }
67 matchers_group_t;
68
61 69 /* Single file association entry. */ /* Single file association entry. */
62 70 typedef struct typedef struct
63 71 { {
64 struct matchers_t *matchers; /* Matching mechanism. */
65 assoc_records_t records; /* Associated programs. */
72 matchers_group_t mg; /* Matching mechanism. */
73 assoc_records_t records; /* Associated programs. */
66 74 } }
67 75 assoc_t; assoc_t;
68 76
 
... ... const char * ft_get_program(const char file[]);
111 119 * Caller should free the result by calling ft_assoc_records_free() on it. */ * Caller should free the result by calling ft_assoc_records_free() on it. */
112 120 assoc_records_t ft_get_all_programs(const char file[]); assoc_records_t ft_get_all_programs(const char file[]);
113 121
114 /* Associates list of comma separated patterns with each item in the list of
115 * comma separated programs either for X or non-X associations and depending on
116 * current execution environment. Takes over ownership of the matchers. */
117 void ft_set_programs(struct matchers_t *matchers, const char programs[],
118 int for_x, int in_x);
122 /* Associates a group of matchers with each item in the list of comma separated
123 * programs either for X or non-X associations and depending on current
124 * execution environment. Takes over ownership of the matchers group. */
125 void ft_set_programs(matchers_group_t mg, const char programs[], int for_x,
126 int in_x);
119 127
120 128 /* Viewers. */ /* Viewers. */
121 129
 
... ... struct strlist_t ft_get_viewers(const char file[]);
132 140 * Caller should free the result by calling ft_assoc_records_free() on it. */ * Caller should free the result by calling ft_assoc_records_free() on it. */
133 141 assoc_records_t ft_get_all_viewers(const char file[]); assoc_records_t ft_get_all_viewers(const char file[]);
134 142
135 /* Associates list of comma separated patterns with each item in the list of
136 * comma separated viewers. */
137 void ft_set_viewers(struct matchers_t *matchers, const char viewers[]);
143 /* Associates a group of matchers with each item in the list of comma separated
144 * viewers. Takes ownership over the matchers group. */
145 void ft_set_viewers(matchers_group_t mg, const char viewers[]);
138 146
139 147 /* Guesses kind of viewer from the invocation command. The parameter can be /* Guesses kind of viewer from the invocation command. The parameter can be
140 148 * empty or NULL in which case textual kind is implied. Returns the kind. */ * empty or NULL in which case textual kind is implied. Returns the kind. */
 
... ... void ft_assoc_record_add_all(assoc_records_t *assocs,
156 164 /* After this call the structure contains NULL values. */ /* After this call the structure contains NULL values. */
157 165 void ft_assoc_records_free(assoc_records_t *records); void ft_assoc_records_free(assoc_records_t *records);
158 166
167 /* Matchers group. */
168
169 /* Parses comma-separated list of matchers. Returns zero on success and sets
170 * *error to NULL, otherwise *error is set to an error message. */
171 int ft_mg_from_string(const char str[], matchers_group_t *mg, char **error);
172
173 /* Turns group of matchers into a comma-separated list. Returns NULL on
174 * errors. */
175 char * ft_mg_to_string(const matchers_group_t *mg);
176
159 177 #endif /* VIFM__FILETYPE_H__ */ #endif /* VIFM__FILETYPE_H__ */
160 178
161 179 /* vim: set tabstop=2 softtabstop=2 shiftwidth=2 noexpandtab cinoptions-=(0 : */ /* vim: set tabstop=2 softtabstop=2 shiftwidth=2 noexpandtab cinoptions-=(0 : */
File tests/filetype/test.c changed (mode: 100644) (index 693386a6d..f8ac9572c)
1 1 #include "test.h" #include "test.h"
2 2
3 3 #include "../../src/int/file_magic.h" #include "../../src/int/file_magic.h"
4 #include "../../src/utils/matchers.h"
5 4 #include "../../src/filetype.h" #include "../../src/filetype.h"
6 5
7 6 void void
8 7 set_programs(const char pattern[], const char programs[], int for_x, int in_x) set_programs(const char pattern[], const char programs[], int for_x, int in_x)
9 8 { {
10 9 char *error; char *error;
11 matchers_t *ms;
10 matchers_group_t mg;
11 assert_success(ft_mg_from_string(pattern, &mg, &error));
12 assert_string_equal(NULL, error);
12 13
13 assert_non_null(ms = matchers_alloc(pattern, 0, 1, "", &error));
14 assert_null(error);
15
16 ft_set_programs(ms, programs, for_x, in_x);
14 ft_set_programs(mg, programs, for_x, in_x);
17 15 } }
18 16
19 17 void void
20 18 set_viewers(const char pattern[], const char viewers[]) set_viewers(const char pattern[], const char viewers[])
21 19 { {
22 20 char *error; char *error;
23 matchers_t *ms;
24
25 assert_non_null(ms = matchers_alloc(pattern, 0, 1, "", &error));
26 assert_null(error);
21 matchers_group_t mg;
22 assert_success(ft_mg_from_string(pattern, &mg, &error));
23 assert_string_equal(NULL, error);
27 24
28 ft_set_viewers(ms, viewers);
25 ft_set_viewers(mg, viewers);
29 26 } }
30 27
31 28 int int
File tests/menus/filetypes.c changed (mode: 100644) (index 316d0c7eb..d729fa98d)
14 14 #include "../../src/modes/wk.h" #include "../../src/modes/wk.h"
15 15 #include "../../src/ui/ui.h" #include "../../src/ui/ui.h"
16 16 #include "../../src/utils/fs.h" #include "../../src/utils/fs.h"
17 #include "../../src/utils/matchers.h"
18 17 #include "../../src/cmd_core.h" #include "../../src/cmd_core.h"
19 18 #include "../../src/filelist.h" #include "../../src/filelist.h"
20 19 #include "../../src/filetype.h" #include "../../src/filetype.h"
 
... ... TEST(unknown_key_is_ignored)
113 112 TEST(pseudo_entry_is_always_present_for_directories) TEST(pseudo_entry_is_always_present_for_directories)
114 113 { {
115 114 char *error; char *error;
116 matchers_t *ms = matchers_alloc("{bla-*/}", 0, 1, "", &error);
117 assert_non_null(ms);
115 matchers_group_t mg;
116 assert_success(ft_mg_from_string("{bla-*/}", &mg, &error));
117 assert_string_equal(NULL, error);
118 118
119 ft_set_programs(ms, "abc-run %c", 0, 0);
119 ft_set_programs(mg, "abc-run %c", 0, 0);
120 120
121 121 assert_success(cmds_dispatch("filetype bla-dir/", &lwin, CIT_COMMAND)); assert_success(cmds_dispatch("filetype bla-dir/", &lwin, CIT_COMMAND));
122 122
 
... ... TEST(no_menu_if_no_handlers)
137 137 TEST(filetypes_menu) TEST(filetypes_menu)
138 138 { {
139 139 char *error; char *error;
140 matchers_t *ms = matchers_alloc("{a,b,c}", 0, 1, "", &error);
141 assert_non_null(ms);
140 matchers_group_t mg;
141 assert_success(ft_mg_from_string("{a,b,c}", &mg, &error));
142 assert_string_equal(NULL, error);
142 143
143 ft_set_programs(ms, "abc-run %c", 0, 0);
144 ft_set_programs(mg, "abc-run %c", 0, 0);
144 145
145 146 assert_success(cmds_dispatch("filetype b", &lwin, CIT_COMMAND)); assert_success(cmds_dispatch("filetype b", &lwin, CIT_COMMAND));
146 147
 
... ... TEST(filetypes_menu)
153 154 TEST(fileviewers_menu) TEST(fileviewers_menu)
154 155 { {
155 156 char *error; char *error;
156 matchers_t *ms = matchers_alloc("{a,b,c}", 0, 1, "", &error);
157 assert_non_null(ms);
157 matchers_group_t mg;
158 assert_success(ft_mg_from_string("{a,b,c}", &mg, &error));
159 assert_string_equal(NULL, error);
158 160
159 ft_set_viewers(ms, "abc-view %c");
161 ft_set_viewers(mg, "abc-view %c");
160 162
161 163 assert_success(cmds_dispatch("fileviewer c", &lwin, CIT_COMMAND)); assert_success(cmds_dispatch("fileviewer c", &lwin, CIT_COMMAND));
162 164
File tests/misc/quickview.c changed (mode: 100644) (index a0f480810..058798e3e)
17 17 #include "../../src/ui/ui.h" #include "../../src/ui/ui.h"
18 18 #include "../../src/utils/file_streams.h" #include "../../src/utils/file_streams.h"
19 19 #include "../../src/utils/fs.h" #include "../../src/utils/fs.h"
20 #include "../../src/utils/matchers.h"
21 20 #include "../../src/utils/str.h" #include "../../src/utils/str.h"
22 21 #include "../../src/utils/string_array.h" #include "../../src/utils/string_array.h"
23 22 #include "../../src/filelist.h" #include "../../src/filelist.h"
 
... ... TEST(no_extra_line_without_extra_padding)
54 53
55 54 TEST(preview_can_match_agains_full_paths) TEST(preview_can_match_agains_full_paths)
56 55 { {
57 char *error;
58 matchers_t *ms;
59
60 56 ft_init(NULL); ft_init(NULL);
61 57
62 assert_non_null(ms = matchers_alloc("{{*/*}}", 0, 1, "", &error));
63 assert_null(error);
58 char *error;
59 matchers_group_t mg;
60 assert_success(ft_mg_from_string("{{*/*}}", &mg, &error));
61 assert_string_equal(NULL, error);
64 62
65 ft_set_viewers(ms, "the-viewer");
63 ft_set_viewers(mg, "the-viewer");
66 64
67 65 assert_string_equal("the-viewer", assert_string_equal("the-viewer",
68 66 qv_get_viewer(TEST_DATA_PATH "/read/two-lines")); qv_get_viewer(TEST_DATA_PATH "/read/two-lines"));
 
... ... TEST(preview_can_match_agains_full_paths)
72 70
73 71 TEST(preview_prg_overrules_fileviewer) TEST(preview_prg_overrules_fileviewer)
74 72 { {
75 char *error;
76 matchers_t *ms;
77
78 73 ft_init(NULL); ft_init(NULL);
79 74
80 assert_non_null(ms = matchers_alloc("file", 0, 1, "", &error));
81 assert_null(error);
75 char *error;
76 matchers_group_t mg;
77 assert_success(ft_mg_from_string("file", &mg, &error));
78 assert_string_equal(NULL, error);
82 79
83 ft_set_viewers(ms, "the-viewer");
80 ft_set_viewers(mg, "the-viewer");
84 81
85 82 assert_string_equal("the-viewer", qv_get_viewer("file")); assert_string_equal("the-viewer", qv_get_viewer("file"));
86 83 update_string(&curr_view->preview_prg, "override"); update_string(&curr_view->preview_prg, "override");
 
... ... TEST(quick_view_picks_parent_directory_if_there_is_a_match)
160 157 char path[PATH_MAX + 1]; char path[PATH_MAX + 1];
161 158
162 159 char *error; char *error;
163 matchers_t *ms = matchers_alloc("../", 0, 1, "", &error);
164 assert_non_null(ms);
165 ft_set_viewers(ms, "do something");
160 matchers_group_t mg;
161 assert_success(ft_mg_from_string("../", &mg, &error));
162 assert_string_equal(NULL, error);
163
164 ft_set_viewers(mg, "do something");
166 165
167 166 qv_get_path_to_explore(&entry, path, sizeof(path)); qv_get_path_to_explore(&entry, path, sizeof(path));
168 167 assert_string_equal("/path/..", path); assert_string_equal("/path/..", path);
File tests/misc/running.c changed (mode: 100644) (index 66928ff2e..a32eb5dc6)
17 17 #include "../../src/utils/dynarray.h" #include "../../src/utils/dynarray.h"
18 18 #include "../../src/utils/fs.h" #include "../../src/utils/fs.h"
19 19 #include "../../src/utils/macros.h" #include "../../src/utils/macros.h"
20 #include "../../src/utils/matchers.h"
21 20 #include "../../src/utils/path.h" #include "../../src/utils/path.h"
22 21 #include "../../src/utils/str.h" #include "../../src/utils/str.h"
23 22 #include "../../src/filelist.h" #include "../../src/filelist.h"
 
... ... TEARDOWN()
97 96
98 97 TEST(full_path_regexps_are_handled_for_selection) TEST(full_path_regexps_are_handled_for_selection)
99 98 { {
100 matchers_t *ms;
101 char pattern[PATH_MAX + 16];
102 char *error;
103
104 99 /* Mind that there is no chdir(), this additionally checks that origins are /* Mind that there is no chdir(), this additionally checks that origins are
105 100 * being used by the code. */ * being used by the code. */
106 101
102 char pattern[PATH_MAX + 16];
107 103 snprintf(pattern, sizeof(pattern), "//%s/*//", lwin.curr_dir); snprintf(pattern, sizeof(pattern), "//%s/*//", lwin.curr_dir);
108 ms = matchers_alloc(pattern, 0, 1, "", &error);
109 assert_non_null(ms);
110 ft_set_programs(ms, "echo %f >> " SANDBOX_PATH "/run", 0, 1);
104
105 char *error;
106 matchers_group_t mg;
107 assert_success(ft_mg_from_string(pattern, &mg, &error));
108 assert_string_equal(NULL, error);
109
110 ft_set_programs(mg, "echo %f >> " SANDBOX_PATH "/run", 0, 1);
111 111
112 112 rn_open(&lwin, FHE_NO_RUN); rn_open(&lwin, FHE_NO_RUN);
113 113
 
... ... TEST(full_path_regexps_are_handled_for_selection)
117 117
118 118 TEST(full_path_regexps_are_handled_for_selection2) TEST(full_path_regexps_are_handled_for_selection2)
119 119 { {
120 matchers_t *ms;
121 char pattern[PATH_MAX + 16];
122 char *error;
123
124 120 /* Mind that there is no chdir(), this additionally checks that origins are /* Mind that there is no chdir(), this additionally checks that origins are
125 121 * being used by the code. */ * being used by the code. */
126 122
123 char pattern[PATH_MAX + 16];
127 124 snprintf(pattern, sizeof(pattern), "//%s/*//", lwin.curr_dir); snprintf(pattern, sizeof(pattern), "//%s/*//", lwin.curr_dir);
128 ms = matchers_alloc(pattern, 0, 1, "", &error);
129 assert_non_null(ms);
125
126 char *error;
127 matchers_group_t mg;
128 assert_success(ft_mg_from_string(pattern, &mg, &error));
129 assert_string_equal(NULL, error);
130
130 131 #ifndef _WIN32 #ifndef _WIN32
131 ft_set_programs(ms, "echo > /dev/null %c &", 0, 1);
132 ft_set_programs(mg, "echo > /dev/null %c &", 0, 1);
132 133 #else #else
133 ft_set_programs(ms, "echo > NUL %c &", 0, 1);
134 ft_set_programs(mg, "echo > NUL %c &", 0, 1);
134 135 #endif #endif
135 136
136 137 rn_open(&lwin, FHE_NO_RUN); rn_open(&lwin, FHE_NO_RUN);
 
... ... TEST(can_open_via_plugin)
148 149 "vifm.addhandler{ name = 'open', handler = open }"); "vifm.addhandler{ name = 'open', handler = open }");
149 150
150 151 char *error; char *error;
151 matchers_t *ms = matchers_alloc("*", 0, 1, "", &error);
152 assert_non_null(ms);
153 ft_set_programs(ms, "#vifmtest#open", /*for_x=*/0, /*in_x=*/1);
152 matchers_group_t mg;
153 assert_success(ft_mg_from_string("*", &mg, &error));
154 assert_string_equal(NULL, error);
155
156 ft_set_programs(mg, "#vifmtest#open", /*for_x=*/0, /*in_x=*/1);
154 157
155 158 rn_open(&lwin, FHE_NO_RUN); rn_open(&lwin, FHE_NO_RUN);
156 159
 
... ... TEST(macro_can_be_added_implicitly, IF(not_windows))
428 431 --lwin.selected_files; --lwin.selected_files;
429 432
430 433 char *error; char *error;
431 matchers_t *ms = matchers_alloc("{a}", 0, 1, "", &error);
432 assert_non_null(ms);
434 matchers_group_t mg;
435 assert_success(ft_mg_from_string("{a}", &mg, &error));
436 assert_string_equal(NULL, error);
433 437
434 438 char cmd[PATH_MAX + 1]; char cmd[PATH_MAX + 1];
435 439 snprintf(cmd, sizeof(cmd), "%s a", script_path); snprintf(cmd, sizeof(cmd), "%s a", script_path);
436 ft_set_programs(ms, cmd, 0, 0);
440 ft_set_programs(mg, cmd, 0, 0);
437 441
438 442 rn_open(&lwin, FHE_NO_RUN); rn_open(&lwin, FHE_NO_RUN);
439 443
 
... ... TEST(handler_can_be_matched_by_a_prefix, IF(not_windows))
453 457 --lwin.selected_files; --lwin.selected_files;
454 458
455 459 char *error; char *error;
456 matchers_t *ms = matchers_alloc("{a}", 0, 1, "", &error);
457 assert_non_null(ms);
460 matchers_group_t mg;
461 assert_success(ft_mg_from_string("{a}", &mg, &error));
462 assert_string_equal(NULL, error);
458 463
459 464 char cmd[PATH_MAX + 1]; char cmd[PATH_MAX + 1];
460 465 snprintf(cmd, sizeof(cmd), "{wrong}no-such-cmd a, {right}%s a", script_path); snprintf(cmd, sizeof(cmd), "{wrong}no-such-cmd a, {right}%s a", script_path);
461 ft_set_programs(ms, cmd, 0, 0);
466 ft_set_programs(mg, cmd, 0, 0);
462 467
463 468 rn_open_with_match(&lwin, script_path, 0); rn_open_with_match(&lwin, script_path, 0);
464 469
 
... ... TEST(selection_multi_run, IF(not_windows))
475 480 { {
476 481 start_use_script(); start_use_script();
477 482
478 char *error;
479 matchers_t *ms;
480 483 char cmd[PATH_MAX + 1]; char cmd[PATH_MAX + 1];
481 484
482 ms = matchers_alloc("{a}", 0, 1, "", &error);
483 assert_non_null(ms);
485 char *error;
486 matchers_group_t mg;
487
488 assert_success(ft_mg_from_string("{a}", &mg, &error));
489 assert_string_equal(NULL, error);
484 490 snprintf(cmd, sizeof(cmd), "%s a %%c &", script_path); snprintf(cmd, sizeof(cmd), "%s a %%c &", script_path);
485 ft_set_programs(ms, cmd, /*for_x=*/0, /*in_x=*/0);
491 ft_set_programs(mg, cmd, /*for_x=*/0, /*in_x=*/0);
486 492
487 ms = matchers_alloc("{b}", 0, 1, "", &error);
488 assert_non_null(ms);
493 assert_success(ft_mg_from_string("{b}", &mg, &error));
494 assert_string_equal(NULL, error);
489 495 snprintf(cmd, sizeof(cmd), "%s b %%\"c &", script_path); snprintf(cmd, sizeof(cmd), "%s b %%\"c &", script_path);
490 ft_set_programs(ms, cmd, /*for_x=*/0, /*in_x=*/0);
496 ft_set_programs(mg, cmd, /*for_x=*/0, /*in_x=*/0);
491 497
492 498 rn_open(&lwin, FHE_NO_RUN); rn_open(&lwin, FHE_NO_RUN);
493 499
 
... ... static void
703 709 assoc_a(char macro) assoc_a(char macro)
704 710 { {
705 711 char *error; char *error;
706 matchers_t *ms = matchers_alloc("{a}", 0, 1, "", &error);
707 assert_non_null(ms);
712 matchers_group_t mg;
713 assert_success(ft_mg_from_string("{a}", &mg, &error));
714 assert_string_equal(NULL, error);
708 715
709 716 char cmd[PATH_MAX + 1]; char cmd[PATH_MAX + 1];
710 717 snprintf(cmd, sizeof(cmd), "%s a %%%c", script_path, macro); snprintf(cmd, sizeof(cmd), "%s a %%%c", script_path, macro);
711 ft_set_programs(ms, cmd, 0, 0);
718 ft_set_programs(mg, cmd, 0, 0);
712 719 } }
713 720
714 721 static void static void
715 722 assoc_b(char macro) assoc_b(char macro)
716 723 { {
717 724 char *error; char *error;
718 matchers_t *ms = matchers_alloc("{b}", 0, 1, "", &error);
719 assert_non_null(ms);
725 matchers_group_t mg;
726 assert_success(ft_mg_from_string("{b}", &mg, &error));
727 assert_string_equal(NULL, error);
720 728
721 729 char cmd[PATH_MAX + 1]; char cmd[PATH_MAX + 1];
722 730 snprintf(cmd, sizeof(cmd), "%s b %%%c", script_path, macro); snprintf(cmd, sizeof(cmd), "%s b %%%c", script_path, macro);
723 ft_set_programs(ms, cmd, 0, 0);
731 ft_set_programs(mg, cmd, 0, 0);
724 732 } }
725 733
726 734 static void static void
727 735 assoc_common(void) assoc_common(void)
728 736 { {
729 737 char *error; char *error;
730 matchers_t *ms = matchers_alloc("{a,b}", 0, 1, "", &error);
731 assert_non_null(ms);
738 matchers_group_t mg;
739 assert_success(ft_mg_from_string("{a,b}", &mg, &error));
740 assert_string_equal(NULL, error);
732 741
733 742 char cmd[PATH_MAX + 1]; char cmd[PATH_MAX + 1];
734 743 snprintf(cmd, sizeof(cmd), "%s common %%f", script_path); snprintf(cmd, sizeof(cmd), "%s common %%f", script_path);
735 ft_set_programs(ms, cmd, 0, 0);
744 ft_set_programs(mg, cmd, 0, 0);
736 745 } }
737 746
738 747 static void static void
739 748 assoc(const char pattern[], const char cmd[]) assoc(const char pattern[], const char cmd[])
740 749 { {
741 750 char *error; char *error;
742 matchers_t *ms = matchers_alloc(pattern, 0, 1, "", &error);
743 assert_non_null(ms);
751 matchers_group_t mg;
752 assert_success(ft_mg_from_string(pattern, &mg, &error));
753 assert_string_equal(NULL, error);
744 754
745 ft_set_programs(ms, cmd, 0, 0);
755 ft_set_programs(mg, cmd, 0, 0);
746 756 } }
747 757
748 758 /* vim: set tabstop=2 softtabstop=2 shiftwidth=2 noexpandtab cinoptions-=(0 : */ /* vim: set tabstop=2 softtabstop=2 shiftwidth=2 noexpandtab cinoptions-=(0 : */
File tests/misc/view_mode.c changed (mode: 100644) (index 7269143c9..17871eeb7)
12 12 #include "../../src/modes/wk.h" #include "../../src/modes/wk.h"
13 13 #include "../../src/ui/quickview.h" #include "../../src/ui/quickview.h"
14 14 #include "../../src/ui/ui.h" #include "../../src/ui/ui.h"
15 #include "../../src/utils/matchers.h"
16 15 #include "../../src/utils/str.h" #include "../../src/utils/str.h"
17 16 #include "../../src/utils/string_array.h" #include "../../src/utils/string_array.h"
18 17 #include "../../src/cmd_core.h" #include "../../src/cmd_core.h"
 
... ... TEST(previewprg_is_applied)
331 330 update_string(&lwin.preview_prg, "echo previewprg_is_applied%%"); update_string(&lwin.preview_prg, "echo previewprg_is_applied%%");
332 331
333 332 char *error; char *error;
334 matchers_t *ms = matchers_alloc("*", 0, 1, "", &error);
335 assert_non_null(ms);
336 ft_set_viewers(ms, "echo viewer%%");
333 matchers_group_t mg;
334 assert_success(ft_mg_from_string("*", &mg, &error));
335 assert_string_equal(NULL, error);
336
337 ft_set_viewers(mg, "echo viewer%%");
337 338
338 339 make_abs_path(lwin.curr_dir, sizeof(lwin.curr_dir), TEST_DATA_PATH, "read", make_abs_path(lwin.curr_dir, sizeof(lwin.curr_dir), TEST_DATA_PATH, "read",
339 340 NULL); NULL);
 
... ... start_view_mode(const char pattern[], const char viewers[],
398 399 if(viewers != NULL) if(viewers != NULL)
399 400 { {
400 401 char *error; char *error;
401 matchers_t *ms = matchers_alloc(pattern, 0, 1, "", &error);
402 assert_non_null(ms);
403 ft_set_viewers(ms, viewers);
402 matchers_group_t mg;
403 assert_success(ft_mg_from_string(pattern, &mg, &error));
404 assert_string_equal(NULL, error);
405
406 ft_set_viewers(mg, viewers);
404 407 } }
405 408
406 409 make_abs_path(lwin.curr_dir, sizeof(lwin.curr_dir), base_dir, sub_path, NULL); make_abs_path(lwin.curr_dir, sizeof(lwin.curr_dir), base_dir, sub_path, NULL);
File tests/misc/vifminfo.c changed (mode: 100644) (index bd9d8987e..242a27ca2)
15 15 #include "../../src/ui/column_view.h" #include "../../src/ui/column_view.h"
16 16 #include "../../src/ui/ui.h" #include "../../src/ui/ui.h"
17 17 #include "../../src/utils/matcher.h" #include "../../src/utils/matcher.h"
18 #include "../../src/utils/matchers.h"
19 18 #include "../../src/utils/parson.h" #include "../../src/utils/parson.h"
20 19 #include "../../src/utils/str.h" #include "../../src/utils/str.h"
21 20 #include "../../src/cmd_core.h" #include "../../src/cmd_core.h"
 
... ... TEST(view_sorting_is_read_from_vifminfo)
90 89 TEST(filetypes_are_deduplicated) TEST(filetypes_are_deduplicated)
91 90 { {
92 91 struct stat first, second; struct stat first, second;
93 char *error;
94 matchers_t *ms;
95 92
96 93 cfg.vifm_info = VINFO_FILETYPES; cfg.vifm_info = VINFO_FILETYPES;
97 94 cmds_init(); cmds_init();
98 95
96 char *error;
97 matchers_group_t mg;
98
99 99 /* Add a filetype. */ /* Add a filetype. */
100 ms = matchers_alloc("*.c", 0, 1, "", &error);
101 assert_non_null(ms);
102 ft_set_programs(ms, "{Description}com,,mand,{descr2}cmd", 0, 1);
100 assert_success(ft_mg_from_string("*.c", &mg, &error));
101 assert_string_equal(NULL, error);
102 ft_set_programs(mg, "{Description}com,,mand,{descr2}cmd", 0, 1);
103 103
104 104 /* Write it first time. */ /* Write it first time. */
105 105 write_info_file(); write_info_file();
 
... ... TEST(filetypes_are_deduplicated)
107 107 assert_success(stat(SANDBOX_PATH "/vifminfo.json", &first)); assert_success(stat(SANDBOX_PATH "/vifminfo.json", &first));
108 108
109 109 /* Add filetype again (as if it was read from vifmrc). */ /* Add filetype again (as if it was read from vifmrc). */
110 ms = matchers_alloc("*.c", 0, 1, "", &error);
111 assert_non_null(ms);
112 ft_set_programs(ms, "{Description}com,,mand,{descr2}cmd", 0, 1);
110 assert_success(ft_mg_from_string("*.c", &mg, &error));
111 assert_string_equal(NULL, error);
112 ft_set_programs(mg, "{Description}com,,mand,{descr2}cmd", 0, 1);
113 113
114 114 /* Update vifminfo second time. */ /* Update vifminfo second time. */
115 115 write_info_file(); write_info_file();
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