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 48759ef05d1f28ce9c145ad36c10ee7ae1e5a81c

Implement deep copying in ops and fops
This adds flags and implements deep copying at this level, but nothing
other than fops tests are using the flags at this point.
Author: xaizek
Author date (UTC): 2026-02-15 16:32
Committer name: xaizek
Committer date (UTC): 2026-02-19 09:45
Parent(s): f31ca6a4e8123b55ca2a75f309ccc5e04deac666
Signing key: 99DC5E4DB05F6BE2
Tree: cf15e8be8965111d1aff4c458dfc9fbc42c1109e
File Lines added Lines deleted
src/fops_common.c 3 3
src/fops_common.h 2 1
src/fops_cpmv.c 22 14
src/fops_cpmv.h 1 0
src/fops_misc.c 7 5
src/fops_put.c 2 2
src/ops.c 9 4
src/ops.h 3 2
tests/fileops/cpmv_files.c 180 0
File src/fops_common.c changed (mode: 100644) (index b8d297662..1f90e8e1e)
... ... fops_is_dir_entry(const char full_path[], const struct dirent *dentry)
922 922
923 923 int int
924 924 fops_enqueue_marked_files(ops_t *ops, view_t *view, const char dst_hint[], fops_enqueue_marked_files(ops_t *ops, view_t *view, const char dst_hint[],
925 int to_trash)
925 int to_trash, int deep)
926 926 { {
927 927 int nmarked_files = 0; int nmarked_files = 0;
928 928 dir_entry_t *entry = NULL; dir_entry_t *entry = NULL;
 
... ... fops_enqueue_marked_files(ops_t *ops, view_t *view, const char dst_hint[],
938 938 if(to_trash) if(to_trash)
939 939 { {
940 940 char *const trash_dir = trash_pick_dir(entry->origin); char *const trash_dir = trash_pick_dir(entry->origin);
941 ops_enqueue(ops, full_path, trash_dir);
941 ops_enqueue(ops, full_path, trash_dir, deep);
942 942 free(trash_dir); free(trash_dir);
943 943 } }
944 944 else else
945 945 { {
946 ops_enqueue(ops, full_path, dst_hint);
946 ops_enqueue(ops, full_path, dst_hint, deep);
947 947 } }
948 948
949 949 ++nmarked_files; ++nmarked_files;
File src/fops_common.h changed (mode: 100644) (index 3e2a37cfc..0b4100710)
... ... typedef struct
45 45 int move; /* Whether this is a move operation. */ int move; /* Whether this is a move operation. */
46 46 int force; /* Whether destination files should be removed. */ int force; /* Whether destination files should be removed. */
47 47 int skip; /* Skip files that already exist at destination. */ int skip; /* Skip files that already exist at destination. */
48 int deep; /* Follow symbolic links in the source of copy. */
48 49 char **sel_list; /* Full paths of files to be processed. */ char **sel_list; /* Full paths of files to be processed. */
49 50 size_t sel_list_len; /* Number of files to process (sel_list size). */ size_t sel_list_len; /* Number of files to process (sel_list size). */
50 51 char path[PATH_MAX + 1]; /* Path at which processing should take place. */ char path[PATH_MAX + 1]; /* Path at which processing should take place. */
 
... ... int fops_is_dir_entry(const char full_path[], const struct dirent* dentry);
133 134 /* Adds marked files to the ops. Considers UI cancellation. dst_hint can be /* Adds marked files to the ops. Considers UI cancellation. dst_hint can be
134 135 * NULL. Returns number of files enqueued. */ * NULL. Returns number of files enqueued. */
135 136 int fops_enqueue_marked_files(ops_t *ops, struct view_t *view, int fops_enqueue_marked_files(ops_t *ops, struct view_t *view,
136 const char dst_hint[], int to_trash);
137 const char dst_hint[], int to_trash, int deep);
137 138
138 139 /* Allocates opt_t structure and configures it as needed. Returns pointer to /* Allocates opt_t structure and configures it as needed. Returns pointer to
139 140 * newly allocated structure, which should be freed by free_ops(). */ * newly allocated structure, which should be freed by free_ops(). */
File src/fops_cpmv.c changed (mode: 100644) (index 3aca949f5..549224f0d)
... ... verify_args_t;
51 51
52 52 static int cp_file(const char src_dir[], const char dst_dir[], const char src[], static int cp_file(const char src_dir[], const char dst_dir[], const char src[],
53 53 const char dst[], CopyMoveLikeOp op, int cancellable, ops_t *ops, const char dst[], CopyMoveLikeOp op, int cancellable, ops_t *ops,
54 int force);
54 int force, int deep);
55 55 static int is_erroneous(view_t *view, const char dst_dir[], int force); static int is_erroneous(view_t *view, const char dst_dir[], int force);
56 56 static int cpmv_prepare(view_t *view, char ***list, int *nlines, static int cpmv_prepare(view_t *view, char ***list, int *nlines,
57 57 CopyMoveLikeOp op, int ignore_conflicts, char undo_msg[], CopyMoveLikeOp op, int ignore_conflicts, char undo_msg[],
 
... ... static const char * cmlo_to_str(CopyMoveLikeOp op);
64 64 static void cpmv_files_in_bg(bg_op_t *bg_op, void *arg); static void cpmv_files_in_bg(bg_op_t *bg_op, void *arg);
65 65 static void set_cpmv_bg_descr(bg_op_t *bg_op, bg_args_t *args, size_t i); static void set_cpmv_bg_descr(bg_op_t *bg_op, bg_args_t *args, size_t i);
66 66 static void cpmv_file_in_bg(ops_t *ops, const char src[], const char dst[], static void cpmv_file_in_bg(ops_t *ops, const char src[], const char dst[],
67 int move, int force, int skip, int from_trash, const char dst_dir[]);
67 int move, int force, int skip, int deep, int from_trash,
68 const char dst_dir[]);
68 69 static int cp_file_f(const char src[], const char dst[], CopyMoveLikeOp op, static int cp_file_f(const char src[], const char dst[], CopyMoveLikeOp op,
69 int bg, int cancellable, ops_t *ops, int force);
70 int bg, int cancellable, ops_t *ops, int force, int deep);
70 71
71 72 int int
72 73 fops_cpmv(view_t *view, char *list[], int nlines, CopyMoveLikeOp op, int flags) fops_cpmv(view_t *view, char *list[], int nlines, CopyMoveLikeOp op, int flags)
73 74 { {
74 75 const int force = (flags & CMLF_FORCE); const int force = (flags & CMLF_FORCE);
75 76 const int skip = (flags & CMLF_SKIP); const int skip = (flags & CMLF_SKIP);
77 const int deep = (flags & CMLF_DEEP);
76 78
77 79 int err; int err;
78 80 int nmarked_files; int nmarked_files;
 
... ... fops_cpmv(view_t *view, char *list[], int nlines, CopyMoveLikeOp op, int flags)
122 124 return 0; return 0;
123 125 } }
124 126
125 nmarked_files = fops_enqueue_marked_files(ops, view, dst_dir, 0);
127 nmarked_files =
128 fops_enqueue_marked_files(ops, view, dst_dir, /*to_trash=*/0, deep);
126 129
127 130 un_group_open(undo_msg); un_group_open(undo_msg);
128 131 i = 0; i = 0;
 
... ... fops_cpmv(view_t *view, char *list[], int nlines, CopyMoveLikeOp op, int flags)
188 191 else else
189 192 { {
190 193 err = cp_file(entry->origin, dst_dir, entry->name, dst, op, 1, ops, err = cp_file(entry->origin, dst_dir, entry->name, dst, op, 1, ops,
191 0);
194 0, deep);
192 195 } }
193 196
194 197 ops_advance(ops, err == 0); ops_advance(ops, err == 0);
 
... ... fops_replace_entry(ops_t *ops, view_t *src, const dir_entry_t *src_entry,
265 268 { {
266 269 /* Not forcing as destination path shouldn't exist. */ /* Not forcing as destination path shouldn't exist. */
267 270 if(cp_file_f(src_full, dst_full, CMLO_COPY, /*bg=*/0, /*cancellable=*/1, if(cp_file_f(src_full, dst_full, CMLO_COPY, /*bg=*/0, /*cancellable=*/1,
268 ops, /*force=*/0) == 0 && !dst_exists)
271 ops, /*force=*/0, /*deep=*/0) == 0 && !dst_exists)
269 272 { {
270 273 /* Update the destination entry to not be fake. */ /* Update the destination entry to not be fake. */
271 274 replace_string(&dst_entry->name, src_entry->name); replace_string(&dst_entry->name, src_entry->name);
 
... ... fops_replace_entry(ops_t *ops, view_t *src, const dir_entry_t *src_entry,
280 283 * parts. */ * parts. */
281 284 static int static int
282 285 cp_file(const char src_dir[], const char dst_dir[], const char src[], cp_file(const char src_dir[], const char dst_dir[], const char src[],
283 const char dst[], CopyMoveLikeOp op, int cancellable, ops_t *ops, int force)
286 const char dst[], CopyMoveLikeOp op, int cancellable, ops_t *ops, int force,
287 int deep)
284 288 { {
285 289 char full_src[PATH_MAX + 1], full_dst[PATH_MAX + 1]; char full_src[PATH_MAX + 1], full_dst[PATH_MAX + 1];
286 290
287 291 to_canonic_path(src, src_dir, full_src, sizeof(full_src)); to_canonic_path(src, src_dir, full_src, sizeof(full_src));
288 292 to_canonic_path(dst, dst_dir, full_dst, sizeof(full_dst)); to_canonic_path(dst, dst_dir, full_dst, sizeof(full_dst));
289 293
290 return cp_file_f(full_src, full_dst, op, 0, cancellable, ops, force);
294 return cp_file_f(full_src, full_dst, op, 0, cancellable, ops, force, deep);
291 295 } }
292 296
293 297 int int
 
... ... fops_cpmv_bg(view_t *view, char *list[], int nlines, int move, int flags)
295 299 { {
296 300 const int force = (flags & CMLF_FORCE); const int force = (flags & CMLF_FORCE);
297 301 const int skip = (flags & CMLF_SKIP); const int skip = (flags & CMLF_SKIP);
302 const int deep = (flags & CMLF_DEEP);
298 303
299 304 int err; int err;
300 305 size_t i; size_t i;
 
... ... fops_cpmv_bg(view_t *view, char *list[], int nlines, int move, int flags)
311 316 args->move = move; args->move = move;
312 317 args->force = force; args->force = force;
313 318 args->skip = skip; args->skip = skip;
319 args->deep = deep;
314 320
315 321 const int ignore_conflicts = (force || skip); const int ignore_conflicts = (force || skip);
316 322 err = cpmv_prepare(view, &list, &args->nlines, move ? CMLO_MOVE : CMLO_COPY, err = cpmv_prepare(view, &list, &args->nlines, move ? CMLO_MOVE : CMLO_COPY,
 
... ... cpmv_files_in_bg(bg_op_t *bg_op, void *arg)
601 607 { {
602 608 const char *const src = args->sel_list[i]; const char *const src = args->sel_list[i];
603 609 const char *const dst = args->list[i]; const char *const dst = args->list[i];
604 ops_enqueue(ops, src, dst);
610 ops_enqueue(ops, src, dst, args->deep);
605 611 } }
606 612 } }
607 613
 
... ... cpmv_files_in_bg(bg_op_t *bg_op, void *arg)
613 619 set_cpmv_bg_descr(bg_op, args, i); set_cpmv_bg_descr(bg_op, args, i);
614 620
615 621 cpmv_file_in_bg(ops, src, dst, args->move, args->force, args->skip, cpmv_file_in_bg(ops, src, dst, args->move, args->force, args->skip,
616 args->is_in_trash[i], args->path);
622 args->deep, args->is_in_trash[i], args->path);
617 623 ++bg_op->done; ++bg_op->done;
618 624 } }
619 625
 
... ... set_cpmv_bg_descr(bg_op_t *bg_op, bg_args_t *args, size_t i)
658 664 /* Actual implementation of background file copying/moving. */ /* Actual implementation of background file copying/moving. */
659 665 static void static void
660 666 cpmv_file_in_bg(ops_t *ops, const char src[], const char dst[], int move, cpmv_file_in_bg(ops_t *ops, const char src[], const char dst[], int move,
661 int force, int skip, int from_trash, const char dst_dir[])
667 int force, int skip, int deep, int from_trash, const char dst_dir[])
662 668 { {
663 669 char dst_full[PATH_MAX + 1]; char dst_full[PATH_MAX + 1];
664 670 snprintf(dst_full, sizeof(dst_full), "%s/%s", dst_dir, dst); snprintf(dst_full, sizeof(dst_full), "%s/%s", dst_dir, dst);
 
... ... cpmv_file_in_bg(ops_t *ops, const char src[], const char dst[], int move,
682 688 } }
683 689 else else
684 690 { {
685 (void)cp_file_f(src, dst_full, CMLO_COPY, 1, 1, ops, 0);
691 (void)cp_file_f(src, dst_full, CMLO_COPY, 1, 1, ops, 0, deep);
686 692 } }
687 693 } }
688 694
 
... ... cpmv_file_in_bg(ops_t *ops, const char src[], const char dst[], int move,
690 696 * non-zero is returned. */ * non-zero is returned. */
691 697 static int static int
692 698 cp_file_f(const char src[], const char dst[], CopyMoveLikeOp op, int bg, cp_file_f(const char src[], const char dst[], CopyMoveLikeOp op, int bg,
693 int cancellable, ops_t *ops, int force)
699 int cancellable, ops_t *ops, int force, int deep)
694 700 { {
695 701 if(strcmp(src, dst) == 0) if(strcmp(src, dst) == 0)
696 702 { {
 
... ... cp_file_f(const char src[], const char dst[], CopyMoveLikeOp op, int bg,
719 725 } }
720 726 } }
721 727
722 void *flags = ops_flags(cancellable ? DF_NONE : DF_NO_CANCEL);
728 int f = (cancellable ? DF_NONE : DF_NO_CANCEL)
729 | (deep ? DF_DEEP_COPY : DF_NONE);
730 void *flags = ops_flags(f);
723 731 OpsResult result = perform_operation(file_op, ops, flags, src, dst); OpsResult result = perform_operation(file_op, ops, flags, src, dst);
724 732 if(result != OPS_SUCCEEDED) if(result != OPS_SUCCEEDED)
725 733 { {
File src/fops_cpmv.h changed (mode: 100644) (index 03f0d118a..53e657cfd)
... ... typedef enum
40 40 CMLF_NONE = 0x00, /* None of the other options. */ CMLF_NONE = 0x00, /* None of the other options. */
41 41 CMLF_FORCE = 0x01, /* Remove destination if it already exists. */ CMLF_FORCE = 0x01, /* Remove destination if it already exists. */
42 42 CMLF_SKIP = 0x02, /* Skip paths that already exist at destination. */ CMLF_SKIP = 0x02, /* Skip paths that already exist at destination. */
43 CMLF_DEEP = 0x04, /* Follow symbolic links in the source of copy. */
43 44 } }
44 45 CopyMoveLikeFlags; CopyMoveLikeFlags;
45 46
File src/fops_misc.c changed (mode: 100644) (index 52de3f3a4..67de96895)
... ... fops_delete(view_t *view, int reg, int use_trash)
160 160 ops = fops_get_ops(OP_REMOVE, use_trash ? "deleting" : "Deleting", curr_dir, ops = fops_get_ops(OP_REMOVE, use_trash ? "deleting" : "Deleting", curr_dir,
161 161 curr_dir); curr_dir);
162 162
163 nmarked_files = fops_enqueue_marked_files(ops, view, NULL, use_trash);
163 nmarked_files =
164 fops_enqueue_marked_files(ops, view, NULL, use_trash, /*deep=*/0);
164 165
165 166 entry = NULL; entry = NULL;
166 167 i = 0; i = 0;
 
... ... delete_files_in_bg(bg_op_t *bg_op, void *arg)
407 408 { {
408 409 const char *const src = args->sel_list[i]; const char *const src = args->sel_list[i];
409 410 char *trash_dir = (args->use_trash ? trash_pick_dir(src) : args->path); char *trash_dir = (args->use_trash ? trash_pick_dir(src) : args->path);
410 ops_enqueue(ops, src, trash_dir);
411 ops_enqueue(ops, src, trash_dir, /*deep=*/0);
411 412 if(trash_dir != args->path) if(trash_dir != args->path)
412 413 { {
413 414 free(trash_dir); free(trash_dir);
 
... ... retarget_many(view_t *view, char *files[], int nfiles)
696 697 { {
697 698 char full_path[PATH_MAX + 1]; char full_path[PATH_MAX + 1];
698 699 get_full_path_of(entry, sizeof(full_path), full_path); get_full_path_of(entry, sizeof(full_path), full_path);
699 ops_enqueue(ops, full_path, full_path);
700 ops_enqueue(ops, full_path, full_path, /*deep=*/0);
700 701 } }
701 702
702 703 int i = 0; int i = 0;
 
... ... fops_clone(view_t *view, char *list[], int nlines, int force, int copies)
843 844 ops_t *ops = fops_get_ops(OP_COPY, "Cloning", curr_dir, ops_t *ops = fops_get_ops(OP_COPY, "Cloning", curr_dir,
844 845 with_dir ? list[0] : curr_dir); with_dir ? list[0] : curr_dir);
845 846
846 int nmarked_files = fops_enqueue_marked_files(ops, view, dst_path, 0);
847 int nmarked_files =
848 fops_enqueue_marked_files(ops, view, dst_path, /*to_trash=*/0, /*deep=*/0);
847 849
848 850 const int custom_fnames = (nlines > 0); const int custom_fnames = (nlines > 0);
849 851
 
... ... fops_chown(int u, int g, uid_t uid, gid_t gid)
1411 1413 replace_home_part(curr_dir)); replace_home_part(curr_dir));
1412 1414
1413 1415 ops = fops_get_ops(OP_CHOWN, "re-owning", curr_dir, curr_dir); ops = fops_get_ops(OP_CHOWN, "re-owning", curr_dir, curr_dir);
1414 (void)fops_enqueue_marked_files(ops, view, NULL, 0);
1416 (void)fops_enqueue_marked_files(ops, view, NULL, /*to_trash=*/0, /*deep=*/0);
1415 1417
1416 1418 fops_append_marked_files(view, undo_msg, NULL); fops_append_marked_files(view, undo_msg, NULL);
1417 1419 un_group_open(undo_msg); un_group_open(undo_msg);
File src/fops_put.c changed (mode: 100644) (index 5d997cc6d..e0322df78)
... ... put_files_in_bg(bg_op_t *bg_op, void *arg)
235 235 { {
236 236 const char *const src = args->sel_list[i]; const char *const src = args->sel_list[i];
237 237 const char *const dst = args->list[i]; const char *const dst = args->list[i];
238 ops_enqueue(ops, src, dst);
238 ops_enqueue(ops, src, dst, /*deep=*/0);
239 239 } }
240 240 } }
241 241
 
... ... initiate_put_files(view_t *view, int at, CopyMoveLikeOp op, const char descr[],
346 346
347 347 for(i = 0; i < reg->nfiles && !ui_cancellation_requested(); ++i) for(i = 0; i < reg->nfiles && !ui_cancellation_requested(); ++i)
348 348 { {
349 ops_enqueue(put_confirm.ops, reg->files[i], dst_dir);
349 ops_enqueue(put_confirm.ops, reg->files[i], dst_dir, /*deep=*/0);
350 350 } }
351 351
352 352 ui_cancellation_pop(); ui_cancellation_pop();
File src/ops.c changed (mode: 100644) (index d8e263933..347717889)
... ... ops_describe(const ops_t *ops)
218 218 } }
219 219
220 220 void void
221 ops_enqueue(ops_t *ops, const char src[], const char dst[])
221 ops_enqueue(ops_t *ops, const char src[], const char dst[], int deep)
222 222 { {
223 223 ++ops->total; ++ops->total;
224 224
 
... ... ops_enqueue(ops_t *ops, const char src[], const char dst[])
264 264 } }
265 265 } }
266 266
267 ioeta_calculate(ops->estim, src, ops->shallow_eta, /*deep=*/0);
267 ioeta_calculate(ops->estim, src, ops->shallow_eta, deep);
268 268 } }
269 269
270 270 void void
 
... ... op_cp(ops_t *ops, void *data, const char src[], const char dst[],
514 514 ConflictAction conflict_action) ConflictAction conflict_action)
515 515 { {
516 516 const int cancellable = ((data_flags(data) & DF_NO_CANCEL) == 0); const int cancellable = ((data_flags(data) & DF_NO_CANCEL) == 0);
517 const int deep_copy = ((data_flags(data) & DF_DEEP_COPY) != 0);
517 518 const int fast_file_cloning = (ops == NULL) const int fast_file_cloning = (ops == NULL)
518 519 ? cfg.fast_file_cloning ? cfg.fast_file_cloning
519 520 : ops->fast_file_cloning; : ops->fast_file_cloning;
 
... ... op_cp(ops_t *ops, void *data, const char src[], const char dst[],
535 536 } }
536 537
537 538 snprintf(cmd, sizeof(cmd), snprintf(cmd, sizeof(cmd),
538 "cp %s %s -R " PRESERVE_FLAGS " %s %s",
539 "cp %s %s %s -R " PRESERVE_FLAGS " %s %s",
539 540 (conflict_action == CA_FAIL) ? NO_CLOBBER : "", (conflict_action == CA_FAIL) ? NO_CLOBBER : "",
540 541 fast_file_cloning ? REFLINK_AUTO : "", fast_file_cloning ? REFLINK_AUTO : "",
542 deep_copy ? "-L" : "",
541 543 escaped_src, escaped_dst); escaped_src, escaped_dst);
542 544 LOG_INFO_MSG("Running cp command: \"%s\"", cmd); LOG_INFO_MSG("Running cp command: \"%s\"", cmd);
543 545 OpsResult result = run_operation_command(ops, cmd, cancellable); OpsResult result = run_operation_command(ops, cmd, cancellable);
 
... ... op_cp(ops_t *ops, void *data, const char src[], const char dst[],
567 569 free(escaped_src); free(escaped_src);
568 570 free(escaped_dst); free(escaped_dst);
569 571
570 if(is_vista_and_above())
572 if(!deep_copy && is_vista_and_above())
573 {
571 574 strcat(cmd, "/B "); strcat(cmd, "/B ");
575 }
572 576 if(conflict_action != CA_FAIL) if(conflict_action != CA_FAIL)
573 577 { {
574 578 strcat(cmd, "/Y "); strcat(cmd, "/Y ");
 
... ... op_cp(ops_t *ops, void *data, const char src[], const char dst[],
596 600 .arg4 = { .arg4 = {
597 601 .fast_file_cloning = fast_file_cloning, .fast_file_cloning = fast_file_cloning,
598 602 .data_sync = data_sync, .data_sync = data_sync,
603 .deep_copying = deep_copy,
599 604 }, },
600 605 }; };
601 606 return exec_io_op(ops, &ior_cp, &args, cancellable); return exec_io_op(ops, &ior_cp, &args, cancellable);
File src/ops.h changed (mode: 100644) (index 97201ab6e..810ebad4a)
... ... typedef enum
62 62 DF_NONE = 0, /* No flags. */ DF_NONE = 0, /* No flags. */
63 63 DF_MAKE_PARENTS = 1 << 0, /* Parent directories should be created. */ DF_MAKE_PARENTS = 1 << 0, /* Parent directories should be created. */
64 64 DF_NO_CANCEL = 1 << 1, /* Cancellation is not enabled for the operation. */ DF_NO_CANCEL = 1 << 1, /* Cancellation is not enabled for the operation. */
65 DF_LIMIT_VALUE = 1 << 2, /* Indirect size of the flags. */
65 DF_DEEP_COPY = 1 << 2, /* Copying should dereference source symlinks. */
66 DF_LIMIT_VALUE = 1 << 3, /* Indirect size of the flags. */
66 67 } }
67 68 DataFlags; DataFlags;
68 69
 
... ... const char * ops_describe(const ops_t *ops);
152 153
153 154 /* Puts new item to the ops. Destination argument is a hint to optimize /* Puts new item to the ops. Destination argument is a hint to optimize
154 155 * estimating performance, it can be NULL. */ * estimating performance, it can be NULL. */
155 void ops_enqueue(ops_t *ops, const char src[], const char dst[]);
156 void ops_enqueue(ops_t *ops, const char src[], const char dst[], int deep);
156 157
157 158 /* Advances ops to the next item. */ /* Advances ops to the next item. */
158 159 void ops_advance(ops_t *ops, int succeeded); void ops_advance(ops_t *ops, int succeeded);
File tests/fileops/cpmv_files.c changed (mode: 100644) (index 0199e4cd9..2cc9cba5d)
... ... TEST(broken_link_behaves_like_a_regular_file_on_conflict, IF(not_windows))
543 543 } }
544 544 } }
545 545
546 TEST(deep_copy_file_link, IF(not_windows), REPEAT(2))
547 {
548 cfg.use_system_calls = STIC_TEST_PARAM;
549
550 char new_fname[] = "copy";
551 char *list[] = { &new_fname[0] };
552
553 const char *contents = "target contents";
554
555 make_file("target", contents);
556
557 update_string(&lwin.dir_entry[0].name, "good-file-symlink");
558 assert_success(make_symlink("target", "good-file-symlink"));
559
560 lwin.dir_entry[0].marked = 1;
561 lwin.dir_entry[0].type = FT_LINK;
562 (void)fops_cpmv(&lwin, list, ARRAY_LEN(list), CMLO_COPY, CMLF_DEEP);
563
564 restore_cwd(saved_cwd);
565 saved_cwd = save_cwd();
566
567 assert_true(is_symlink(SANDBOX_PATH "/good-file-symlink"));
568 assert_false(is_symlink(SANDBOX_PATH "/copy"));
569
570 const char *lines[] = { contents };
571 file_is(SANDBOX_PATH "/copy", lines, ARRAY_LEN(lines));
572
573 remove_file(SANDBOX_PATH "/good-file-symlink");
574 remove_file(SANDBOX_PATH "/copy");
575 remove_file(SANDBOX_PATH "/target");
576 }
577
578 TEST(deep_copy_dir_link, IF(not_windows), REPEAT(2))
579 {
580 cfg.use_system_calls = STIC_TEST_PARAM;
581
582 char new_fname[] = "copy";
583 char *list[] = { &new_fname[0] };
584
585 create_dir("target");
586
587 update_string(&lwin.dir_entry[0].name, "good-dir-symlink");
588 assert_success(make_symlink("target", "good-dir-symlink"));
589
590 lwin.dir_entry[0].marked = 1;
591 lwin.dir_entry[0].type = FT_LINK;
592 (void)fops_cpmv(&lwin, list, ARRAY_LEN(list), CMLO_COPY, CMLF_DEEP);
593
594 restore_cwd(saved_cwd);
595 saved_cwd = save_cwd();
596
597 assert_true(is_symlink(SANDBOX_PATH "/good-dir-symlink"));
598 assert_false(is_symlink(SANDBOX_PATH "/copy"));
599
600 remove_file(SANDBOX_PATH "/good-dir-symlink");
601 remove_dir(SANDBOX_PATH "/copy");
602 remove_dir(SANDBOX_PATH "/target");
603 }
604
605 TEST(deep_copy_link_in_dir, IF(not_windows), REPEAT(2))
606 {
607 cfg.use_system_calls = STIC_TEST_PARAM;
608
609 char new_fname[] = "copy";
610 char *list[] = { &new_fname[0] };
611
612 create_dir("target");
613 create_dir("dir");
614
615 update_string(&lwin.dir_entry[0].name, "dir");
616 assert_success(make_symlink("../target", "dir/dir-symlink"));
617
618 lwin.dir_entry[0].marked = 1;
619 lwin.dir_entry[0].type = FT_DIR;
620 (void)fops_cpmv(&lwin, list, ARRAY_LEN(list), CMLO_COPY, CMLF_DEEP);
621
622 restore_cwd(saved_cwd);
623 saved_cwd = save_cwd();
624
625 assert_true(is_symlink(SANDBOX_PATH "/dir/dir-symlink"));
626 assert_false(is_symlink(SANDBOX_PATH "/copy/dir-symlink"));
627
628 remove_file(SANDBOX_PATH "/dir/dir-symlink");
629 remove_dir(SANDBOX_PATH "/dir");
630
631 remove_dir(SANDBOX_PATH "/copy/dir-symlink");
632 remove_dir(SANDBOX_PATH "/copy");
633
634 remove_dir(SANDBOX_PATH "/target");
635 }
636
637 TEST(deep_copy_bad_link, IF(not_windows), REPEAT(2))
638 {
639 cfg.use_system_calls = STIC_TEST_PARAM;
640
641 char new_fname[] = "copy";
642 char *list[] = { &new_fname[0] };
643
644 update_string(&lwin.dir_entry[0].name, "bad-symlink");
645 assert_success(make_symlink("notarget", "bad-symlink"));
646
647 lwin.dir_entry[0].marked = 1;
648 lwin.dir_entry[0].type = FT_LINK;
649 (void)fops_cpmv(&lwin, list, ARRAY_LEN(list), CMLO_COPY, CMLF_DEEP);
650
651 restore_cwd(saved_cwd);
652 saved_cwd = save_cwd();
653
654 assert_true(is_symlink(SANDBOX_PATH "/bad-symlink"));
655 remove_file(SANDBOX_PATH "/bad-symlink");
656
657 /* There is no actual file to copy but the symlink itself. */
658 if(cfg.use_system_calls)
659 {
660 assert_true(is_symlink(SANDBOX_PATH "/copy"));
661 remove_file(SANDBOX_PATH "/copy");
662 }
663 }
664
665 TEST(deep_copy_file_link_loop, IF(not_windows), REPEAT(2))
666 {
667 cfg.use_system_calls = STIC_TEST_PARAM;
668
669 char new_fname[] = "copy";
670 char *list[] = { &new_fname[0] };
671
672 update_string(&lwin.dir_entry[0].name, "link");
673 assert_success(make_symlink("link", "link"));
674
675 lwin.dir_entry[0].marked = 1;
676 lwin.dir_entry[0].type = FT_LINK;
677 (void)fops_cpmv(&lwin, list, ARRAY_LEN(list), CMLO_COPY, CMLF_DEEP);
678
679 restore_cwd(saved_cwd);
680 saved_cwd = save_cwd();
681
682 assert_true(is_symlink(SANDBOX_PATH "/link"));
683 remove_file(SANDBOX_PATH "/link");
684
685 /* There is no actual file to copy but the symlink itself. */
686 if(cfg.use_system_calls)
687 {
688 assert_true(is_symlink(SANDBOX_PATH "/copy"));
689 remove_file(SANDBOX_PATH "/copy");
690 }
691 }
692
693 TEST(deep_copy_dir_link_loop, IF(not_windows), REPEAT(2))
694 {
695 cfg.use_system_calls = STIC_TEST_PARAM;
696
697 char new_fname[] = "copy";
698 char *list[] = { &new_fname[0] };
699
700 create_dir("dir");
701
702 update_string(&lwin.dir_entry[0].name, "dir");
703 assert_success(make_symlink("../dir", "dir/parent-symlink"));
704
705 lwin.dir_entry[0].marked = 1;
706 lwin.dir_entry[0].type = FT_DIR;
707 (void)fops_cpmv(&lwin, list, ARRAY_LEN(list), CMLO_COPY, CMLF_DEEP);
708
709 restore_cwd(saved_cwd);
710 saved_cwd = save_cwd();
711
712 assert_true(is_symlink(SANDBOX_PATH "/dir/parent-symlink"));
713
714 remove_file(SANDBOX_PATH "/dir/parent-symlink");
715 remove_dir(SANDBOX_PATH "/dir");
716
717 /* There is no actual file to copy but the symlink itself. */
718 if(cfg.use_system_calls)
719 {
720 assert_true(is_symlink(SANDBOX_PATH "/copy/parent-symlink"));
721 remove_file(SANDBOX_PATH "/copy/parent-symlink");
722 }
723 remove_dir(SANDBOX_PATH "/copy");
724 }
725
546 726 static void static void
547 727 check_directory_clash(int parent_to_child, CopyMoveLikeOp op) check_directory_clash(int parent_to_child, CopyMoveLikeOp op)
548 728 { {
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