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 c09a11e74125a8b9b70acc7018659a22134251b8

Add -deep parameter to :put command
It makes copying resolve symbolic links.

Thanks to Jose Riha (a.k.a. jose1711) and an anonymous at Vifm
Q2A site.

See
https://q2a.vifm.info/1237/how-can-i-use-yy-and-p-but-with-dereferenced-links
Author: xaizek
Author date (UTC): 2026-02-18 09:45
Committer name: xaizek
Committer date (UTC): 2026-02-19 09:45
Parent(s): a17658e7c7d813035bf1a878f5b8f4d536940b18
Signing key: 99DC5E4DB05F6BE2
Tree: 23a322de619c62ce983577d706a390a30ae2a3a5
File Lines added Lines deleted
ChangeLog 3 3
data/man/vifm.1 4 1
data/vim/doc/app/vifm-app.txt 3 1
src/cmd_completion.c 8 3
src/cmd_completion.h 1 0
src/cmd_handlers.c 26 5
src/fops_put.c 15 10
src/fops_put.h 2 2
src/modes/normal.c 2 1
tests/commands/completion.c 6 0
tests/commands/misc_fops.c 49 0
tests/fileops/put_files.c 31 31
File ChangeLog changed (mode: 100644) (index 7caecc4dd..2c71eedb2)
34 34 Added example of handling .deb-files to sample vifmrc. Patch by Kirill Added example of handling .deb-files to sample vifmrc. Patch by Kirill
35 35 Rekhov. Rekhov.
36 36
37 Added -deep parameter to :copy command to resolve symbolic links while
38 copying. Thanks to Jose Riha (a.k.a. jose1711) and an anonymous at Vifm
39 Q2A site.
37 Added -deep parameter to :copy and :put commands that makes them resolve
38 symbolic links while copying. Thanks to Jose Riha (a.k.a. jose1711) and
39 an anonymous at Vifm Q2A site.
40 40
41 41 Updated utf8proc to v2.11.2. Updated utf8proc to v2.11.2.
42 42
File data/man/vifm.1 changed (mode: 100644) (index 88abd8222..7b015ad6e)
1 .TH VIFM 1 "17 February 2026" "vifm 0.15"
1 .TH VIFM 1 "18 February 2026" "vifm 0.15"
2 2 .\" --------------------------------------------------------------------------- .\" ---------------------------------------------------------------------------
3 3 .SH NAME .SH NAME
4 4 .\" --------------------------------------------------------------------------- .\" ---------------------------------------------------------------------------
 
... ... The [line] can be used to pick node in a tree-view. "!" moves files
2845 2845 During this operation no confirmation dialogs will be shown, all checks During this operation no confirmation dialogs will be shown, all checks
2846 2846 are performed beforehand. are performed beforehand.
2847 2847 .TP .TP
2848 .BI ":[line]pu[t][!] -deep ..."
2849 see "\-deep parameter" below.
2850 .TP
2848 2851 .BI " :pwd" .BI " :pwd"
2849 2852 .TP .TP
2850 2853 .BI :pw[d] .BI :pw[d]
File data/vim/doc/app/vifm-app.txt changed (mode: 100644) (index 32dafcfd6..65c907afe)
1 *vifm-app.txt* For Vifm version 0.15 Last change: 2026 February 17
1 *vifm-app.txt* For Vifm version 0.15 Last change: 2026 February 18
2 2
3 3 Email for bugs and suggestions: <xaizek@posteo.net> Email for bugs and suggestions: <xaizek@posteo.net>
4 4
 
... ... order using the '.' operator. Any whitespace is ignored.
2431 2431 from their original location instead of copying them. During this from their original location instead of copying them. During this
2432 2432 operation no confirmation dialogs will be shown, all checks are performed operation no confirmation dialogs will be shown, all checks are performed
2433 2433 beforehand. beforehand.
2434 :[line]pu[t][!] -deep ...
2435 see |vifm-deep-param|.
2434 2436
2435 2437 :pw[d] *vifm-:pwd* *vifm-:pw* :pw[d] *vifm-:pwd* *vifm-:pw*
2436 2438 display the present working directory. display the present working directory.
File src/cmd_completion.c changed (mode: 100644) (index 06b533bb8..f0d1aa7ed)
... ... non_path_completion(completion_data_t *data)
317 317 } }
318 318 } }
319 319 else if(is_option(data->cmd_info) && (id == COM_COPY || id == COM_MOVE || else if(is_option(data->cmd_info) && (id == COM_COPY || id == COM_MOVE ||
320 id == COM_ALINK || id == COM_RLINK))
320 id == COM_ALINK || id == COM_RLINK || id == COM_PUT))
321 321 { {
322 322 complete_option(id, arg); complete_option(id, arg);
323 323 } }
 
... ... non_path_completion(completion_data_t *data)
328 328 complete_view(arg); complete_view(arg);
329 329 } }
330 330 } }
331 else
331 else if(id != COM_PUT)
332 332 { {
333 /* Perform path completion. */
333 334 return -1; return -1;
334 335 } }
335 336
 
... ... complete_winrun(const char str[])
1130 1131 complete_from_string_list(str, win_marks, ARRAY_LEN(win_marks), 0); complete_from_string_list(str, win_marks, ARRAY_LEN(win_marks), 0);
1131 1132 } }
1132 1133
1133 /* Completes an option of :copy/:move/:alink/:rlink. */
1134 /* Completes an option of :copy/:move/:alink/:rlink/:put. */
1134 1135 static void static void
1135 1136 complete_option(int cmd_id, const char str[]) complete_option(int cmd_id, const char str[])
1136 1137 { {
 
... ... complete_option(int cmd_id, const char str[])
1147 1148 opts = lines; opts = lines;
1148 1149 opt_count = 2; opt_count = 2;
1149 1150 break; break;
1151 case COM_PUT:
1152 opts = &lines[0];
1153 opt_count = 1;
1154 break;
1150 1155
1151 1156 default: default:
1152 1157 opts = &lines[1]; opts = &lines[1];
File src/cmd_completion.h changed (mode: 100644) (index 4dc2542ff..0e6f09dd5)
... ... enum
62 62 COM_MOVE, COM_MOVE,
63 63 COM_PLUGIN, COM_PLUGIN,
64 64 COM_PUSHD, COM_PUSHD,
65 COM_PUT,
65 66 COM_RENAME, COM_RENAME,
66 67 COM_RLINK, COM_RLINK,
67 68 COM_SELECT, COM_SELECT,
File src/cmd_handlers.c changed (mode: 100644) (index 57ce4b96c..707b86aad)
... ... const cmd_add_t cmds_list[] = {
747 747 .descr = "push onto directory stack", .descr = "push onto directory stack",
748 748 .flags = HAS_EMARK | HAS_QUOTED_ARGS | HAS_COMMENT | HAS_ENVVARS, .flags = HAS_EMARK | HAS_QUOTED_ARGS | HAS_COMMENT | HAS_ENVVARS,
749 749 .handler = &pushd_cmd, .min_args = 0, .max_args = 2, }, .handler = &pushd_cmd, .min_args = 0, .max_args = 2, },
750 { .name = "put", .abbr = "pu", .id = -1,
750 { .name = "put", .abbr = "pu", .id = COM_PUT,
751 751 .descr = "paste files from a register", .descr = "paste files from a register",
752 752 .flags = HAS_EMARK | HAS_RANGE | HAS_BG_FLAG, .flags = HAS_EMARK | HAS_RANGE | HAS_BG_FLAG,
753 753 .handler = &put_cmd, .min_args = 0, .max_args = 1, }, .handler = &put_cmd, .min_args = 0, .max_args = 1, },
 
... ... put_cmd(const cmd_info_t *cmd_info)
4144 4144 int reg = DEFAULT_REG_NAME; int reg = DEFAULT_REG_NAME;
4145 4145 const int at = get_at(curr_view, cmd_info); const int at = get_at(curr_view, cmd_info);
4146 4146
4147 if(cmd_info->argc == 1)
4147 const int move = cmd_info->emark;
4148
4149 int argc = cmd_info->argc;
4150 char **argv = cmd_info->argv;
4151 const int flags = parse_cpmv_flags(&argc, &argv);
4152 if(flags < 0)
4153 {
4154 return CMDS_ERR_CUSTOM;
4155 }
4156 if(flags & CMLF_SKIP)
4157 {
4158 ui_sb_err("-skip doesn't apply to putting");
4159 return CMDS_ERR_CUSTOM;
4160 }
4161 const int deep = ((flags & CMLF_DEEP) != 0);
4162 if(move && deep)
4163 {
4164 ui_sb_err("-deep doesn't apply to moving");
4165 return CMDS_ERR_CUSTOM;
4166 }
4167
4168 if(argc == 1)
4148 4169 { {
4149 const int error = get_reg(cmd_info->argv[0], &reg);
4170 const int error = get_reg(argv[0], &reg);
4150 4171 if(error != 0) if(error != 0)
4151 4172 { {
4152 4173 return error; return error;
 
... ... put_cmd(const cmd_info_t *cmd_info)
4155 4176
4156 4177 if(cmd_info->bg) if(cmd_info->bg)
4157 4178 { {
4158 return fops_put_bg(curr_view, at, reg, cmd_info->emark) != 0;
4179 return fops_put_bg(curr_view, at, reg, move, deep) != 0;
4159 4180 } }
4160 4181
4161 return fops_put(curr_view, at, reg, cmd_info->emark) != 0;
4182 return fops_put(curr_view, at, reg, move, deep) != 0;
4162 4183 } }
4163 4184
4164 4185 static int static int
File src/fops_put.c changed (mode: 100644) (index e0322df78..fa2d634cf)
... ... conflict_prompt_data_t;
61 61
62 62 static void put_files_in_bg(bg_op_t *bg_op, void *arg); static void put_files_in_bg(bg_op_t *bg_op, void *arg);
63 63 static int initiate_put_files(view_t *view, int at, CopyMoveLikeOp op, static int initiate_put_files(view_t *view, int at, CopyMoveLikeOp op,
64 const char descr[], int reg_name);
64 const char descr[], int reg_name, int deep);
65 65 static void reset_put_confirm(CopyMoveLikeOp main_op, const char descr[], static void reset_put_confirm(CopyMoveLikeOp main_op, const char descr[],
66 66 const char dst_dir[]); const char dst_dir[]);
67 67 static OPS cmlo_to_op(CopyMoveLikeOp op); static OPS cmlo_to_op(CopyMoveLikeOp op);
 
... ... static struct
103 103 int allow_merge_all; /* Allow merging of files in directories by default. */ int allow_merge_all; /* Allow merging of files in directories by default. */
104 104 int merge; /* Merge conflicting directory once. */ int merge; /* Merge conflicting directory once. */
105 105 int merge_all; /* Merge all conflicting directories. */ int merge_all; /* Merge all conflicting directories. */
106 int deep; /* Follow symbolic links in the source of copy. */
106 107 ops_t *ops; /* Currently running operation. */ ops_t *ops; /* Currently running operation. */
107 108 char *dst_name; /* Name of destination file. */ char *dst_name; /* Name of destination file. */
108 109 char *dst_dir; /* Destination path. */ char *dst_dir; /* Destination path. */
 
... ... static struct
112 113 put_confirm; put_confirm;
113 114
114 115 int int
115 fops_put(view_t *view, int at, int reg_name, int move)
116 fops_put(view_t *view, int at, int reg_name, int move, int deep)
116 117 { {
117 118 const CopyMoveLikeOp op = move ? CMLO_MOVE : CMLO_COPY; const CopyMoveLikeOp op = move ? CMLO_MOVE : CMLO_COPY;
118 119 const char *const descr = move ? "Putting" : "putting"; const char *const descr = move ? "Putting" : "putting";
119 return initiate_put_files(view, at, op, descr, reg_name);
120 return initiate_put_files(view, at, op, descr, reg_name, deep);
120 121 } }
121 122
122 123 int int
123 fops_put_bg(view_t *view, int at, int reg_name, int move)
124 fops_put_bg(view_t *view, int at, int reg_name, int move, int deep)
124 125 { {
125 126 char task_desc[COMMAND_GROUP_INFO_LEN]; char task_desc[COMMAND_GROUP_INFO_LEN];
126 127 size_t task_desc_len; size_t task_desc_len;
 
... ... fops_put_bg(view_t *view, int at, int reg_name, int move)
148 149
149 150 args = calloc(1, sizeof(*args)); args = calloc(1, sizeof(*args));
150 151 args->move = move; args->move = move;
152 args->deep = deep;
151 153 copy_str(args->path, sizeof(args->path), dst_dir); copy_str(args->path, sizeof(args->path), dst_dir);
152 154
153 155 snprintf(task_desc, sizeof(task_desc), "%cut in %s: ", move ? 'P' : 'p', snprintf(task_desc, sizeof(task_desc), "%cut in %s: ", move ? 'P' : 'p',
 
... ... put_files_in_bg(bg_op_t *bg_op, void *arg)
235 237 { {
236 238 const char *const src = args->sel_list[i]; const char *const src = args->sel_list[i];
237 239 const char *const dst = args->list[i]; const char *const dst = args->list[i];
238 ops_enqueue(ops, src, dst, /*deep=*/0);
240 ops_enqueue(ops, src, dst, args->deep);
239 241 } }
240 242 } }
241 243
 
... ... put_files_in_bg(bg_op_t *bg_op, void *arg)
266 268 } }
267 269
268 270 bg_op_set_descr(bg_op, src); bg_op_set_descr(bg_op, src);
269 (void)perform_operation(ops->main_op, ops, ops_flags(DF_NONE), src, dst);
271
272 void *flags = ops_flags(args->deep ? DF_DEEP_COPY : DF_NONE);
273 (void)perform_operation(ops->main_op, ops, flags, src, dst);
270 274 } }
271 275
272 276 fops_free_bg_args(args); fops_free_bg_args(args);
 
... ... int
276 280 fops_put_links(view_t *view, int reg_name, int relative) fops_put_links(view_t *view, int reg_name, int relative)
277 281 { {
278 282 const CopyMoveLikeOp op = relative ? CMLO_LINK_REL : CMLO_LINK_ABS; const CopyMoveLikeOp op = relative ? CMLO_LINK_REL : CMLO_LINK_ABS;
279 return initiate_put_files(view, -1, op, "Symlinking", reg_name);
283 return initiate_put_files(view, -1, op, "Symlinking", reg_name, /*deep=*/0);
280 284 } }
281 285
282 286 /* Performs preparations necessary for putting files/links. Returns new value /* Performs preparations necessary for putting files/links. Returns new value
283 287 * for save_msg flag. */ * for save_msg flag. */
284 288 static int static int
285 289 initiate_put_files(view_t *view, int at, CopyMoveLikeOp op, const char descr[], initiate_put_files(view_t *view, int at, CopyMoveLikeOp op, const char descr[],
286 int reg_name)
290 int reg_name, int deep)
287 291 { {
288 292 int i; int i;
289 293 const char *const dst_dir = fops_get_dst_dir(view, at); const char *const dst_dir = fops_get_dst_dir(view, at);
 
... ... initiate_put_files(view_t *view, int at, CopyMoveLikeOp op, const char descr[],
306 310 put_confirm.op = op; put_confirm.op = op;
307 311 put_confirm.reg = reg; put_confirm.reg = reg;
308 312 put_confirm.view = view; put_confirm.view = view;
313 put_confirm.deep = deep;
309 314
310 315 /* Map each element onto itself initially. */ /* Map each element onto itself initially. */
311 316 put_confirm.file_order = reallocarray(NULL, reg->nfiles, put_confirm.file_order = reallocarray(NULL, reg->nfiles,
 
... ... initiate_put_files(view_t *view, int at, CopyMoveLikeOp op, const char descr[],
346 351
347 352 for(i = 0; i < reg->nfiles && !ui_cancellation_requested(); ++i) for(i = 0; i < reg->nfiles && !ui_cancellation_requested(); ++i)
348 353 { {
349 ops_enqueue(put_confirm.ops, reg->files[i], dst_dir, /*deep=*/0);
354 ops_enqueue(put_confirm.ops, reg->files[i], dst_dir, deep);
350 355 } }
351 356
352 357 ui_cancellation_pop(); ui_cancellation_pop();
 
... ... put_next(int force)
700 705 fops_progress_msg("Putting files", put_confirm.index, fops_progress_msg("Putting files", put_confirm.index,
701 706 put_confirm.reg->nfiles); put_confirm.reg->nfiles);
702 707
703 void *flags = ops_flags(DF_NONE);
708 void *flags = ops_flags(put_confirm.deep ? DF_DEEP_COPY : DF_NONE);
704 709
705 710 /* Merging directory on move requires special handling as it can't be done by /* Merging directory on move requires special handling as it can't be done by
706 711 * `mv` and it's better to use the same code regardless of the state of * `mv` and it's better to use the same code regardless of the state of
File src/fops_put.h changed (mode: 100644) (index 9d58d168b..673386c92)
... ... struct view_t;
27 27 /* Puts files from specified register into current directory. at specifies /* Puts files from specified register into current directory. at specifies
28 28 * index of entry to be used to obtain destination path, -1 means current * index of entry to be used to obtain destination path, -1 means current
29 29 * position. Returns new value for save_msg flag. */ * position. Returns new value for save_msg flag. */
30 int fops_put(struct view_t *view, int at, int reg_name, int move);
30 int fops_put(struct view_t *view, int at, int reg_name, int move, int deep);
31 31
32 32 /* Starts background task that puts files from specified register into current /* Starts background task that puts files from specified register into current
33 33 * directory. at specifies index of entry to be used to obtain destination * directory. at specifies index of entry to be used to obtain destination
34 34 * path, -1 means current position. Returns new value for save_msg flag. */ * path, -1 means current position. Returns new value for save_msg flag. */
35 int fops_put_bg(struct view_t *view, int at, int reg_name, int move);
35 int fops_put_bg(struct view_t *view, int at, int reg_name, int move, int deep);
36 36
37 37 /* Like fops_put(), but makes absolute or relative symbolic links to files. /* Like fops_put(), but makes absolute or relative symbolic links to files.
38 38 * Returns new value for save_msg flag. */ * Returns new value for save_msg flag. */
File src/modes/normal.c changed (mode: 100644) (index 72bde22bd..66f43ea95)
... ... cmd_p(key_info_t key_info, keys_info_t *keys_info)
1712 1712 static void static void
1713 1713 call_put_files(key_info_t key_info, int move) call_put_files(key_info_t key_info, int move)
1714 1714 { {
1715 curr_stats.save_msg = fops_put(curr_view, -1, def_reg(key_info.reg), move);
1715 curr_stats.save_msg =
1716 fops_put(curr_view, -1, def_reg(key_info.reg), move, /*deep=*/0);
1716 1717 ui_views_reload_filelists(); ui_views_reload_filelists();
1717 1718 } }
1718 1719
File tests/commands/completion.c changed (mode: 100644) (index 9e9e55b2a..6e2b7809f)
... ... TEST(highlight_columns_are_completed)
628 628 curr_stats.vlua = NULL; curr_stats.vlua = NULL;
629 629 } }
630 630
631 TEST(put_is_completed)
632 {
633 ASSERT_COMPLETION(L"put ", L"put ");
634 ASSERT_COMPLETION(L"put -", L"put -deep");
635 }
636
631 637 TEST(command_options_are_completed) TEST(command_options_are_completed)
632 638 { {
633 639 ASSERT_COMPLETION(L"move -", L"move -skip"); ASSERT_COMPLETION(L"move -", L"move -skip");
File tests/commands/misc_fops.c changed (mode: 100644) (index 5dbb34120..763f13714)
... ... TEST(putting_files_works)
201 201 regs_reset(); regs_reset();
202 202 } }
203 203
204 TEST(put_invocation)
205 {
206 /* Invalid register name. */
207 ui_sb_msg("");
208 assert_failure(cmds_dispatch1("put asdf", &lwin, CIT_COMMAND));
209 assert_string_equal("Trailing characters", ui_sb_last());
210
211 ui_sb_msg("");
212 assert_failure(cmds_dispatch1("put -bla", &lwin, CIT_COMMAND));
213 assert_string_equal("Unrecognized :command option: -bla", ui_sb_last());
214
215 ui_sb_msg("");
216 assert_failure(cmds_dispatch1("put -skip", &lwin, CIT_COMMAND));
217 assert_string_equal("-skip doesn't apply to putting", ui_sb_last());
218
219 ui_sb_msg("");
220 assert_failure(cmds_dispatch1("put! -deep", &lwin, CIT_COMMAND));
221 assert_string_equal("-deep doesn't apply to moving", ui_sb_last());
222 }
223
224 TEST(deep_put, IF(not_windows))
225 {
226 char path[PATH_MAX + 1];
227
228 regs_init();
229
230 assert_success(os_mkdir(SANDBOX_PATH "/dst", 0700));
231 make_abs_path(lwin.curr_dir, sizeof(lwin.curr_dir), SANDBOX_PATH, "dst", cwd);
232
233 make_file(SANDBOX_PATH "/file", "contents");
234 make_abs_path(path, sizeof(path), SANDBOX_PATH, "file", cwd);
235 assert_success(make_symlink(path, SANDBOX_PATH "/linked"));
236
237 make_abs_path(path, sizeof(path), SANDBOX_PATH, "linked", cwd);
238 assert_success(regs_append(DEFAULT_REG_NAME, path));
239
240 assert_failure(cmds_dispatch1("put -deep", &lwin, CIT_COMMAND));
241 restore_cwd(saved_cwd);
242 saved_cwd = save_cwd();
243 assert_false(is_symlink(SANDBOX_PATH "/dst/linked"));
244
245 remove_file(SANDBOX_PATH "/dst/linked");
246 remove_dir(SANDBOX_PATH "/dst");
247 remove_file(SANDBOX_PATH "/linked");
248 remove_file(SANDBOX_PATH "/file");
249
250 regs_reset();
251 }
252
204 253 TEST(yank_works_with_ranges) TEST(yank_works_with_ranges)
205 254 { {
206 255 char path[PATH_MAX + 1]; char path[PATH_MAX + 1];
File tests/fileops/put_files.c changed (mode: 100644) (index 3e3a0ee43..23736a889)
... ... TEARDOWN()
71 71
72 72 TEST(put_files_bg_fails_on_wrong_register) TEST(put_files_bg_fails_on_wrong_register)
73 73 { {
74 assert_true(fops_put_bg(&lwin, -1, -1, 0));
74 assert_true(fops_put_bg(&lwin, -1, -1, 0, /*deep=*/0));
75 75 wait_for_bg(); wait_for_bg();
76 76 } }
77 77
78 78 TEST(put_files_bg_fails_on_empty_register) TEST(put_files_bg_fails_on_empty_register)
79 79 { {
80 assert_true(fops_put_bg(&lwin, -1, 'a', 0));
80 assert_true(fops_put_bg(&lwin, -1, 'a', 0, /*deep=*/0));
81 81 wait_for_bg(); wait_for_bg();
82 82 } }
83 83
 
... ... TEST(put_files_bg_fails_on_identical_names_in_a_register)
86 86 assert_success(regs_append('a', TEST_DATA_PATH "/existing-files/a")); assert_success(regs_append('a', TEST_DATA_PATH "/existing-files/a"));
87 87 assert_success(regs_append('a', TEST_DATA_PATH "/rename/a")); assert_success(regs_append('a', TEST_DATA_PATH "/rename/a"));
88 88
89 assert_true(fops_put_bg(&lwin, -1, 'a', 0));
89 assert_true(fops_put_bg(&lwin, -1, 'a', 0, /*deep=*/0));
90 90 wait_for_bg(); wait_for_bg();
91 91 } }
92 92
 
... ... TEST(put_files_bg_fails_on_file_name_conflict)
96 96
97 97 assert_success(regs_append('a', TEST_DATA_PATH "/rename/a")); assert_success(regs_append('a', TEST_DATA_PATH "/rename/a"));
98 98
99 assert_true(fops_put_bg(&lwin, -1, 'a', 0));
99 assert_true(fops_put_bg(&lwin, -1, 'a', 0, /*deep=*/0));
100 100 wait_for_bg(); wait_for_bg();
101 101
102 102 assert_success(unlink(SANDBOX_PATH "/a")); assert_success(unlink(SANDBOX_PATH "/a"));
 
... ... TEST(put_files_bg_copies_files)
106 106 { {
107 107 assert_success(regs_append('a', TEST_DATA_PATH "/existing-files/a")); assert_success(regs_append('a', TEST_DATA_PATH "/existing-files/a"));
108 108
109 assert_int_equal(0, fops_put_bg(&lwin, -1, 'a', 0));
109 assert_int_equal(0, fops_put_bg(&lwin, -1, 'a', 0, /*deep=*/0));
110 110 wait_for_bg(); wait_for_bg();
111 111
112 112 assert_success(unlink(SANDBOX_PATH "/a")); assert_success(unlink(SANDBOX_PATH "/a"));
 
... ... TEST(put_files_bg_skips_nonexistent_source_files)
121 121 assert_success(regs_append('a', SANDBOX_PATH "/dir/b")); assert_success(regs_append('a', SANDBOX_PATH "/dir/b"));
122 122 assert_success(unlink(SANDBOX_PATH "/dir/b")); assert_success(unlink(SANDBOX_PATH "/dir/b"));
123 123
124 assert_int_equal(0, fops_put_bg(&lwin, -1, 'a', 0));
124 assert_int_equal(0, fops_put_bg(&lwin, -1, 'a', 0, /*deep=*/0));
125 125 wait_for_bg(); wait_for_bg();
126 126
127 127 assert_success(unlink(SANDBOX_PATH "/a")); assert_success(unlink(SANDBOX_PATH "/a"));
 
... ... TEST(put_files_bg_demangles_names_of_trashed_files)
140 140 make_abs_path(path, sizeof(path), SANDBOX_PATH, "trash/000_b", saved_cwd); make_abs_path(path, sizeof(path), SANDBOX_PATH, "trash/000_b", saved_cwd);
141 141 assert_success(regs_append('a', path)); assert_success(regs_append('a', path));
142 142
143 assert_int_equal(0, fops_put_bg(&lwin, -1, 'a', 1));
143 assert_int_equal(0, fops_put_bg(&lwin, -1, 'a', 1, /*deep=*/0));
144 144 wait_for_bg(); wait_for_bg();
145 145
146 146 assert_success(unlink(SANDBOX_PATH "/b")); assert_success(unlink(SANDBOX_PATH "/b"));
 
... ... TEST(put_files_copies_files_according_to_tree_structure)
163 163 /* Copy at the top level. Set at to -1. */ /* Copy at the top level. Set at to -1. */
164 164
165 165 lwin.list_pos = 0; lwin.list_pos = 0;
166 (void)fops_put(&lwin, -1, 'a', 0);
166 (void)fops_put(&lwin, -1, 'a', 0, /*deep=*/0);
167 167 restore_cwd(saved_cwd); restore_cwd(saved_cwd);
168 168 saved_cwd = save_cwd(); saved_cwd = save_cwd();
169 169 assert_success(unlink(SANDBOX_PATH "/a")); assert_success(unlink(SANDBOX_PATH "/a"));
170 170
171 171 lwin.list_pos = 0; lwin.list_pos = 0;
172 assert_int_equal(0, fops_put_bg(&lwin, -1, 'a', 0));
172 assert_int_equal(0, fops_put_bg(&lwin, -1, 'a', 0, /*deep=*/0));
173 173 wait_for_bg(); wait_for_bg();
174 174 assert_success(unlink(SANDBOX_PATH "/a")); assert_success(unlink(SANDBOX_PATH "/a"));
175 175
176 176 /* Copy at nested level. Set at to desired position. */ /* Copy at nested level. Set at to desired position. */
177 177
178 (void)fops_put(&lwin, 1, 'a', 0);
178 (void)fops_put(&lwin, 1, 'a', 0, /*deep=*/0);
179 179 restore_cwd(saved_cwd); restore_cwd(saved_cwd);
180 180 saved_cwd = save_cwd(); saved_cwd = save_cwd();
181 181 assert_success(unlink(SANDBOX_PATH "/dir/a")); assert_success(unlink(SANDBOX_PATH "/dir/a"));
182 182
183 183 /* Here target position in 100, which should become 1 automatically. */ /* Here target position in 100, which should become 1 automatically. */
184 assert_int_equal(0, fops_put_bg(&lwin, 100, 'a', 0));
184 assert_int_equal(0, fops_put_bg(&lwin, 100, 'a', 0, /*deep=*/0));
185 185 wait_for_bg(); wait_for_bg();
186 186 assert_success(unlink(SANDBOX_PATH "/dir/a")); assert_success(unlink(SANDBOX_PATH "/dir/a"));
187 187
 
... ... TEST(overwrite_request_accounts_for_target_file_rename)
204 204
205 205 fops_init(&line_prompt, &options_prompt_rename); fops_init(&line_prompt, &options_prompt_rename);
206 206
207 (void)fops_put(&lwin, -1, 'a', 0);
207 (void)fops_put(&lwin, -1, 'a', 0, /*deep=*/0);
208 208 restore_cwd(saved_cwd); restore_cwd(saved_cwd);
209 209 saved_cwd = save_cwd(); saved_cwd = save_cwd();
210 210
 
... ... TEST(abort_stops_operation)
230 230 assert_success(regs_append('a', SANDBOX_PATH "/dir/b")); assert_success(regs_append('a', SANDBOX_PATH "/dir/b"));
231 231
232 232 fops_init(&line_prompt, &options_prompt_abort); fops_init(&line_prompt, &options_prompt_abort);
233 (void)fops_put(&lwin, -1, 'a', 0);
233 (void)fops_put(&lwin, -1, 'a', 0, /*deep=*/0);
234 234 restore_cwd(saved_cwd); restore_cwd(saved_cwd);
235 235 saved_cwd = save_cwd(); saved_cwd = save_cwd();
236 236
 
... ... TEST(rename_on_put)
262 262 assert_success(regs_append('a', path)); assert_success(regs_append('a', path));
263 263
264 264 fops_init(&line_prompt_rec, &options_prompt_rename_rec); fops_init(&line_prompt_rec, &options_prompt_rename_rec);
265 (void)fops_put(&lwin, -1, 'a', 0);
265 (void)fops_put(&lwin, -1, 'a', 0, /*deep=*/0);
266 266 /* Continue the operation. */ /* Continue the operation. */
267 267 rename_cb("b", /*arg=*/NULL); rename_cb("b", /*arg=*/NULL);
268 268
 
... ... TEST(change_mind)
303 303 /* Overwrite #1 -> No -> Skip -> Overwrite #2. */ /* Overwrite #1 -> No -> Skip -> Overwrite #2. */
304 304
305 305 fops_init(&line_prompt, &cm_overwrite); fops_init(&line_prompt, &cm_overwrite);
306 (void)fops_put(&lwin, -1, 'a', 0);
306 (void)fops_put(&lwin, -1, 'a', 0, /*deep=*/0);
307 307 restore_cwd(saved_cwd); restore_cwd(saved_cwd);
308 308 saved_cwd = save_cwd(); saved_cwd = save_cwd();
309 309
 
... ... TEST(broken_link_does_not_stop_putting, IF(not_windows))
331 331
332 332 make_abs_path(lwin.curr_dir, sizeof(lwin.curr_dir), SANDBOX_PATH "/dst", "", make_abs_path(lwin.curr_dir, sizeof(lwin.curr_dir), SANDBOX_PATH "/dst", "",
333 333 saved_cwd); saved_cwd);
334 (void)fops_put(&lwin, -1, 'a', 0);
334 (void)fops_put(&lwin, -1, 'a', 0, /*deep=*/0);
335 335 restore_cwd(saved_cwd); restore_cwd(saved_cwd);
336 336 saved_cwd = save_cwd(); saved_cwd = save_cwd();
337 337
 
... ... TEST(broken_link_behaves_like_a_regular_file_on_conflict, IF(not_windows))
355 355 assert_success(regs_append('a', path)); assert_success(regs_append('a', path));
356 356
357 357 fops_init(&line_prompt, &cm_no); fops_init(&line_prompt, &cm_no);
358 (void)fops_put(&lwin, -1, 'a', /*move=*/1);
358 (void)fops_put(&lwin, -1, 'a', /*move=*/1, /*deep=*/0);
359 359 restore_cwd(saved_cwd); restore_cwd(saved_cwd);
360 360 saved_cwd = save_cwd(); saved_cwd = save_cwd();
361 361
 
... ... parent_overwrite_with_put(int move)
383 383 assert_success(regs_append('a', path)); assert_success(regs_append('a', path));
384 384
385 385 fops_init(&line_prompt, &options_prompt_overwrite); fops_init(&line_prompt, &options_prompt_overwrite);
386 (void)fops_put(&lwin, -1, 'a', move);
386 (void)fops_put(&lwin, -1, 'a', move, /*deep=*/0);
387 387 restore_cwd(saved_cwd); restore_cwd(saved_cwd);
388 388 saved_cwd = save_cwd(); saved_cwd = save_cwd();
389 389
 
... ... double_clash_with_put(int move)
416 416 /* The larger sub-tree should be moved in this case. */ /* The larger sub-tree should be moved in this case. */
417 417
418 418 fops_init(&line_prompt, &options_prompt_overwrite); fops_init(&line_prompt, &options_prompt_overwrite);
419 (void)fops_put(&lwin, -1, 'a', move);
419 (void)fops_put(&lwin, -1, 'a', move, /*deep=*/0);
420 420 restore_cwd(saved_cwd); restore_cwd(saved_cwd);
421 421 saved_cwd = save_cwd(); saved_cwd = save_cwd();
422 422
 
... ... TEST(putting_single_file_moves_cursor_to_that_file)
439 439 assert_success(regs_append('a', path)); assert_success(regs_append('a', path));
440 440
441 441 lwin.list_pos = 0; lwin.list_pos = 0;
442 (void)fops_put(&lwin, -1, 'a', 0);
442 (void)fops_put(&lwin, -1, 'a', 0, /*deep=*/0);
443 443 assert_int_equal(1, lwin.list_pos); assert_int_equal(1, lwin.list_pos);
444 444 restore_cwd(saved_cwd); restore_cwd(saved_cwd);
445 445 saved_cwd = save_cwd(); saved_cwd = save_cwd();
 
... ... TEST(putting_multiple_files_moves_cursor_to_the_first_one_in_sorted_order)
464 464 assert_success(regs_append('a', path)); assert_success(regs_append('a', path));
465 465
466 466 lwin.list_pos = 0; lwin.list_pos = 0;
467 (void)fops_put(&lwin, -1, 'a', 0);
467 (void)fops_put(&lwin, -1, 'a', 0, /*deep=*/0);
468 468 assert_int_equal(1, lwin.list_pos); assert_int_equal(1, lwin.list_pos);
469 469 assert_string_equal("a", get_current_file_name(&lwin)); assert_string_equal("a", get_current_file_name(&lwin));
470 470 restore_cwd(saved_cwd); restore_cwd(saved_cwd);
 
... ... TEST(putting_file_with_conflict_moves_cursor_on_aborting)
490 490
491 491 lwin.list_pos = 0; lwin.list_pos = 0;
492 492 fops_init(&line_prompt, &options_prompt_abort); fops_init(&line_prompt, &options_prompt_abort);
493 (void)fops_put(&lwin, -1, 'a', 0);
493 (void)fops_put(&lwin, -1, 'a', 0, /*deep=*/0);
494 494 assert_int_equal(1, lwin.list_pos); assert_int_equal(1, lwin.list_pos);
495 495 restore_cwd(saved_cwd); restore_cwd(saved_cwd);
496 496 saved_cwd = save_cwd(); saved_cwd = save_cwd();
 
... ... TEST(putting_files_with_conflict_moves_cursor_to_the_last_conflicting_file)
516 516
517 517 lwin.list_pos = 0; lwin.list_pos = 0;
518 518 fops_init(&line_prompt, &options_prompt_skip_all); fops_init(&line_prompt, &options_prompt_skip_all);
519 (void)fops_put(&lwin, -1, 'a', 0);
519 (void)fops_put(&lwin, -1, 'a', 0, /*deep=*/0);
520 520 assert_int_equal(1, lwin.list_pos); assert_int_equal(1, lwin.list_pos);
521 521 assert_string_equal("b", get_current_file_name(&lwin)); assert_string_equal("b", get_current_file_name(&lwin));
522 522 restore_cwd(saved_cwd); restore_cwd(saved_cwd);
 
... ... TEST(putting_files_with_conflict_moves_cursor_to_the_last_renamed_file)
543 543
544 544 lwin.list_pos = 0; lwin.list_pos = 0;
545 545 fops_init(&line_prompt, &options_prompt_rename); fops_init(&line_prompt, &options_prompt_rename);
546 (void)fops_put(&lwin, -1, 'a', 0);
546 (void)fops_put(&lwin, -1, 'a', 0, /*deep=*/0);
547 547 assert_int_equal(1, lwin.list_pos); assert_int_equal(1, lwin.list_pos);
548 548 assert_string_equal("b", get_current_file_name(&lwin)); assert_string_equal("b", get_current_file_name(&lwin));
549 549 restore_cwd(saved_cwd); restore_cwd(saved_cwd);
 
... ... TEST(cursor_is_moved_even_if_no_file_was_processed)
569 569
570 570 lwin.list_pos = 0; lwin.list_pos = 0;
571 571 fops_init(&line_prompt, &options_prompt_skip_all); fops_init(&line_prompt, &options_prompt_skip_all);
572 (void)fops_put(&lwin, -1, 'a', 0);
572 (void)fops_put(&lwin, -1, 'a', 0, /*deep=*/0);
573 573 assert_int_equal(1, lwin.list_pos); assert_int_equal(1, lwin.list_pos);
574 574 assert_string_equal("b", get_current_file_name(&lwin)); assert_string_equal("b", get_current_file_name(&lwin));
575 575 restore_cwd(saved_cwd); restore_cwd(saved_cwd);
 
... ... TEST(show_merge_all_option_if_paths_include_dir)
597 597 assert_success(regs_append('a', path)); assert_success(regs_append('a', path));
598 598
599 599 options_count = 0; options_count = 0;
600 (void)fops_put(&lwin, -1, 'a', 0);
600 (void)fops_put(&lwin, -1, 'a', 0, /*deep=*/0);
601 601 assert_int_equal(8, options_count); assert_int_equal(8, options_count);
602 602
603 603 restore_cwd(saved_cwd); restore_cwd(saved_cwd);
 
... ... TEST(show_merge_all_option_if_paths_include_dir)
607 607 assert_success(regs_append('a', path)); assert_success(regs_append('a', path));
608 608
609 609 options_count = 0; options_count = 0;
610 (void)fops_put(&lwin, -1, 'a', 0);
610 (void)fops_put(&lwin, -1, 'a', 0, /*deep=*/0);
611 611 assert_int_equal(9, options_count); assert_int_equal(9, options_count);
612 612
613 613 restore_cwd(saved_cwd); restore_cwd(saved_cwd);
 
... ... TEST(merging_on_copy_confirms_overwrites)
668 668 merge_prompt_count = 0; merge_prompt_count = 0;
669 669 yes_prompt_count = 0; yes_prompt_count = 0;
670 670
671 (void)fops_put(&lwin, /*at=*/-1, /*reg_name=*/'a', /*move=*/0);
671 (void)fops_put(&lwin, /*at=*/-1, /*reg_name=*/'a', /*move=*/0, /*deep=*/0);
672 672 assert_int_equal(1, merge_prompt_count); assert_int_equal(1, merge_prompt_count);
673 673 assert_int_equal(1, yes_prompt_count); assert_int_equal(1, yes_prompt_count);
674 674
 
... ... TEST(merging_on_move_confirms_overwrites)
704 704 merge_prompt_count = 0; merge_prompt_count = 0;
705 705 yes_prompt_count = 0; yes_prompt_count = 0;
706 706
707 (void)fops_put(&lwin, /*at=*/-1, /*reg_name=*/'a', /*move=*/1);
707 (void)fops_put(&lwin, /*at=*/-1, /*reg_name=*/'a', /*move=*/1, /*deep=*/0);
708 708 assert_int_equal(1, merge_prompt_count); assert_int_equal(1, merge_prompt_count);
709 709 assert_int_equal(1, yes_prompt_count); assert_int_equal(1, yes_prompt_count);
710 710
 
... ... TEST(failure_to_remove_source_due_to_user_does_not_stop_moving)
744 744 merge_prompt_count = 0; merge_prompt_count = 0;
745 745 no_prompt_count = 0; no_prompt_count = 0;
746 746
747 (void)fops_put(&lwin, /*at=*/-1, /*reg_name=*/'a', /*move=*/1);
747 (void)fops_put(&lwin, /*at=*/-1, /*reg_name=*/'a', /*move=*/1, /*deep=*/0);
748 748 assert_int_equal(1, merge_prompt_count); assert_int_equal(1, merge_prompt_count);
749 749 assert_int_equal(2, no_prompt_count); assert_int_equal(2, no_prompt_count);
750 750
 
... ... TEST(put_keeps_unmoved_files_that_were_denied_to_overwrite)
788 788 merge_prompt_count = 0; merge_prompt_count = 0;
789 789 no_prompt_count = 0; no_prompt_count = 0;
790 790
791 (void)fops_put(&lwin, /*at=*/-1, /*reg_name=*/'a', /*move=*/1);
791 (void)fops_put(&lwin, /*at=*/-1, /*reg_name=*/'a', /*move=*/1, /*deep=*/0);
792 792 assert_int_equal(1, merge_prompt_count); assert_int_equal(1, merge_prompt_count);
793 793 assert_int_equal(1, no_prompt_count); assert_int_equal(1, no_prompt_count);
794 794
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