This source file includes following definitions.
- dirsize_status_locate_buttons
- build_dest
- free_link
- free_erase_list
- free_linklist
- is_in_linklist
- check_hardlinks
- make_symlink
- do_compute_dir_size
- panel_compute_totals
- panel_operate_init_totals
- progress_update_one
- real_warn_same_file
- warn_same_file
- check_same_file
- real_do_file_error
- real_query_recursive
- do_file_error
- query_recursive
- query_replace
- do_file_error
- query_recursive
- query_replace
- files_error
- calc_copy_file_progress
- try_remove_file
- move_file_file
- erase_file
- try_erase_dir
- recursive_erase
- check_dir_is_empty
- erase_dir_iff_empty
- erase_dir_after_copy
- do_move_dir_dir
- panel_get_file
- check_single_entry
- panel_operate_generate_prompt
- do_confirm_copy_move
- do_confirm_erase
- operate_single_file
- operate_one_file
- end_bg_process
- attrs_ignore_error
- file_is_symlink_to_dir
- copy_file_file
- copy_dir_dir
- move_dir_dir
- erase_dir
- dirsize_status_init_cb
- dirsize_status_update_cb
- dirsize_status_deinit_cb
- compute_dir_size
- panel_operate
- file_error
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52 #include <config.h>
53
54 #include <ctype.h>
55 #include <errno.h>
56 #include <stdlib.h>
57 #include <stdio.h>
58 #include <string.h>
59 #include <sys/types.h>
60 #include <sys/stat.h>
61 #include <unistd.h>
62
63 #include "lib/global.h"
64 #include "lib/tty/tty.h"
65 #include "lib/tty/key.h"
66 #include "lib/search.h"
67 #include "lib/strutil.h"
68 #include "lib/util.h"
69 #include "lib/vfs/vfs.h"
70 #include "lib/vfs/utilvfs.h"
71 #include "lib/widget.h"
72
73 #include "src/setup.h"
74 #ifdef ENABLE_BACKGROUND
75 #include "src/background.h"
76 #endif
77 #include "src/util.h"
78
79
80 #include "dir.h"
81 #include "tree.h"
82 #include "filemanager.h"
83 #include "layout.h"
84 #include "ioblksize.h"
85
86 #include "file.h"
87
88
89
90
91
92
93 const char *op_names[3] = {
94 N_ ("DialogTitle|Copy"),
95 N_ ("DialogTitle|Move"),
96 N_ ("DialogTitle|Delete"),
97 };
98
99
100
101 #define FILEOP_UPDATE_INTERVAL 2
102 #define FILEOP_STALLING_INTERVAL 4
103 #define FILEOP_UPDATE_INTERVAL_US (FILEOP_UPDATE_INTERVAL * G_USEC_PER_SEC)
104 #define FILEOP_STALLING_INTERVAL_US (FILEOP_STALLING_INTERVAL * G_USEC_PER_SEC)
105
106
107
108
109 typedef struct
110 {
111 const struct vfs_class *vfs;
112 dev_t dev;
113 ino_t ino;
114 mode_t st_mode;
115 vfs_path_t *src_vpath;
116 vfs_path_t *dst_vpath;
117 } link_t;
118
119
120 typedef enum
121 {
122 DEST_NONE = 0,
123 DEST_SHORT_QUERY,
124 DEST_SHORT_KEEP,
125 DEST_SHORT_DELETE,
126 DEST_FULL
127 } dest_status_t;
128
129
130 typedef enum
131 {
132 HARDLINK_OK = 0,
133 HARDLINK_CACHED,
134 HARDLINK_NOTLINK,
135 HARDLINK_UNSUPPORTED,
136 HARDLINK_ERROR,
137 HARDLINK_ABORT
138 } hardlink_status_t;
139
140
141
142
143
144
145
146
147
148
149 static const char *op_names1[] = {
150 N_ ("FileOperation|Copy"),
151 N_ ("FileOperation|Move"),
152 N_ ("FileOperation|Delete"),
153 };
154
155
156
157
158
159
160
161
162
163
164
165 static const char *one_format = N_ ("%o %f%n\"%s\"%m");
166
167 static const char *many_format = N_ ("%o %d %f%m");
168
169 static const char *prompt_parts[] = {
170 N_ ("file"), N_ ("files"), N_ ("directory"), N_ ("directories"), N_ ("files/directories"),
171
172 N_ (" with source mask:")
173 };
174
175
176
177
178
179
180 static GSList *linklist = NULL;
181
182
183 static GQueue *erase_list = NULL;
184
185
186
187
188
189 static GSList *dest_dirs = NULL;
190
191
192
193
194
195 static void
196 dirsize_status_locate_buttons (dirsize_status_msg_t *dsm)
197 {
198 status_msg_t *sm = STATUS_MSG (dsm);
199 Widget *wd = WIDGET (sm->dlg);
200 int y, x;
201 WRect r;
202
203 y = wd->rect.y + 5;
204 x = wd->rect.x;
205
206 if (!dsm->allow_skip)
207 {
208
209 x += (wd->rect.cols - dsm->abort_button->rect.cols) / 2;
210 r = dsm->abort_button->rect;
211 r.y = y;
212 r.x = x;
213 widget_set_size_rect (dsm->abort_button, &r);
214 }
215 else
216 {
217
218 int cols;
219
220 cols = dsm->abort_button->rect.cols + dsm->skip_button->rect.cols + 1;
221 x += (wd->rect.cols - cols) / 2;
222 r = dsm->abort_button->rect;
223 r.y = y;
224 r.x = x;
225 widget_set_size_rect (dsm->abort_button, &r);
226 x += dsm->abort_button->rect.cols + 1;
227 r = dsm->skip_button->rect;
228 r.y = y;
229 r.x = x;
230 widget_set_size_rect (dsm->skip_button, &r);
231 }
232 }
233
234
235
236 static char *
237 build_dest (file_op_context_t *ctx, const char *src, const char *dest, FileProgressStatus *status)
238 {
239 char *s, *q;
240 const char *fnsource;
241
242 *status = FILE_CONT;
243
244 s = g_strdup (src);
245
246
247
248 for (q = s; *q != '\0'; q++)
249 if (*q == '\n')
250 *q = ' ';
251
252 fnsource = x_basename (s);
253
254 if (!mc_search_run (ctx->search_handle, fnsource, 0, strlen (fnsource), NULL))
255 {
256 q = NULL;
257 *status = FILE_SKIP;
258 }
259 else
260 {
261 q = mc_search_prepare_replace_str2 (ctx->search_handle, ctx->dest_mask);
262 if (ctx->search_handle->error != MC_SEARCH_E_OK)
263 {
264 if (ctx->search_handle->error_str != NULL)
265 message (D_ERROR, MSG_ERROR, "%s", ctx->search_handle->error_str);
266
267 *status = FILE_ABORT;
268 }
269 }
270
271 MC_PTR_FREE (s);
272
273 if (*status == FILE_CONT)
274 {
275 char *repl_dest;
276
277 repl_dest = mc_search_prepare_replace_str2 (ctx->search_handle, dest);
278 if (ctx->search_handle->error == MC_SEARCH_E_OK)
279 s = mc_build_filename (repl_dest, q, (char *) NULL);
280 else
281 {
282 if (ctx->search_handle->error_str != NULL)
283 message (D_ERROR, MSG_ERROR, "%s", ctx->search_handle->error_str);
284
285 *status = FILE_ABORT;
286 }
287
288 g_free (repl_dest);
289 }
290
291 g_free (q);
292
293 return s;
294 }
295
296
297
298 static void
299 free_link (void *data)
300 {
301 link_t *lp = (link_t *) data;
302
303 vfs_path_free (lp->src_vpath, TRUE);
304 vfs_path_free (lp->dst_vpath, TRUE);
305 g_free (lp);
306 }
307
308
309
310 static inline void *
311 free_erase_list (GQueue *lp)
312 {
313 if (lp != NULL)
314 g_queue_free_full (lp, free_link);
315
316 return NULL;
317 }
318
319
320
321 static inline void *
322 free_linklist (GSList *lp)
323 {
324 g_slist_free_full (lp, free_link);
325
326 return NULL;
327 }
328
329
330
331 static const link_t *
332 is_in_linklist (const GSList *lp, const vfs_path_t *vpath, const struct stat *sb)
333 {
334 const struct vfs_class *class;
335 ino_t ino = sb->st_ino;
336 dev_t dev = sb->st_dev;
337
338 class = vfs_path_get_last_path_vfs (vpath);
339
340 for (; lp != NULL; lp = (const GSList *) g_slist_next (lp))
341 {
342 const link_t *lnk = (const link_t *) lp->data;
343
344 if (lnk->vfs == class && lnk->ino == ino && lnk->dev == dev)
345 return lnk;
346 }
347
348 return NULL;
349 }
350
351
352
353
354
355
356
357
358
359 static hardlink_status_t
360 check_hardlinks (file_op_context_t *ctx, const vfs_path_t *src_vpath, const struct stat *src_stat,
361 const vfs_path_t *dst_vpath, gboolean *ignore_all)
362 {
363 link_t *lnk;
364 ino_t ino = src_stat->st_ino;
365 dev_t dev = src_stat->st_dev;
366
367 if (src_stat->st_nlink < 2)
368 return HARDLINK_NOTLINK;
369 if ((vfs_file_class_flags (src_vpath) & VFSF_NOLINKS) != 0)
370 return HARDLINK_UNSUPPORTED;
371
372 lnk = (link_t *) is_in_linklist (linklist, src_vpath, src_stat);
373 if (lnk != NULL)
374 {
375 int stat_result;
376 struct stat link_stat;
377
378 stat_result = mc_stat (lnk->src_vpath, &link_stat);
379
380 if (stat_result == 0 && link_stat.st_ino == ino && link_stat.st_dev == dev)
381 {
382 const struct vfs_class *lp_name_class;
383 const struct vfs_class *my_vfs;
384
385 lp_name_class = vfs_path_get_last_path_vfs (lnk->src_vpath);
386 my_vfs = vfs_path_get_last_path_vfs (src_vpath);
387
388 if (lp_name_class == my_vfs)
389 {
390 const struct vfs_class *p_class, *dst_name_class;
391
392 dst_name_class = vfs_path_get_last_path_vfs (dst_vpath);
393 p_class = vfs_path_get_last_path_vfs (lnk->dst_vpath);
394
395 if (dst_name_class == p_class)
396 {
397 gboolean ok;
398
399 while (!(ok = (mc_stat (lnk->dst_vpath, &link_stat) == 0)) && !*ignore_all)
400 {
401 FileProgressStatus status;
402
403 status = file_error (ctx, TRUE, _ ("Cannot stat hardlink source file\n%s"),
404 vfs_path_as_str (lnk->dst_vpath));
405 if (status == FILE_ABORT)
406 return HARDLINK_ABORT;
407 if (status == FILE_RETRY)
408 continue;
409 if (status == FILE_IGNORE_ALL)
410 *ignore_all = TRUE;
411 break;
412 }
413
414
415 if (!ok)
416 return HARDLINK_ERROR;
417
418 while (!(ok = (mc_link (lnk->dst_vpath, dst_vpath) == 0)) && !*ignore_all)
419 {
420 FileProgressStatus status;
421
422 status = file_error (ctx, TRUE, _ ("Cannot create target hardlink\n%s"),
423 vfs_path_as_str (dst_vpath));
424 if (status == FILE_ABORT)
425 return HARDLINK_ABORT;
426 if (status == FILE_RETRY)
427 continue;
428 if (status == FILE_IGNORE_ALL)
429 *ignore_all = TRUE;
430 break;
431 }
432
433
434 return (ok ? HARDLINK_OK : HARDLINK_ERROR);
435 }
436 }
437 }
438
439 if (!*ignore_all)
440 {
441 FileProgressStatus status;
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456 errno = 0;
457 status = file_error (ctx, FALSE, _ ("Cannot create target hardlink\n%s"),
458 vfs_path_as_str (dst_vpath));
459
460 if (status == FILE_ABORT)
461 return HARDLINK_ABORT;
462
463 if (status == FILE_IGNORE_ALL)
464 *ignore_all = TRUE;
465 }
466
467 return HARDLINK_ERROR;
468 }
469
470 lnk = g_try_new (link_t, 1);
471 if (lnk != NULL)
472 {
473 lnk->vfs = vfs_path_get_last_path_vfs (src_vpath);
474 lnk->ino = ino;
475 lnk->dev = dev;
476 lnk->st_mode = 0;
477 lnk->src_vpath = vfs_path_clone (src_vpath);
478 lnk->dst_vpath = vfs_path_clone (dst_vpath);
479
480 linklist = g_slist_prepend (linklist, lnk);
481 }
482
483 return HARDLINK_CACHED;
484 }
485
486
487
488
489
490
491
492
493
494
495 static FileProgressStatus
496 make_symlink (file_op_context_t *ctx, const vfs_path_t *src_vpath, const vfs_path_t *dst_vpath)
497 {
498 const char *src_path;
499 const char *dst_path;
500 char link_target[MC_MAXPATHLEN];
501 int len;
502 FileProgressStatus return_status;
503 struct stat dst_stat;
504 gboolean dst_is_symlink;
505 vfs_path_t *link_target_vpath = NULL;
506
507 src_path = vfs_path_as_str (src_vpath);
508 dst_path = vfs_path_as_str (dst_vpath);
509
510 dst_is_symlink = (mc_lstat (dst_vpath, &dst_stat) == 0) && S_ISLNK (dst_stat.st_mode);
511
512 retry_src_readlink:
513 len = mc_readlink (src_vpath, link_target, sizeof (link_target) - 1);
514 if (len < 0)
515 {
516 if (ctx->ignore_all)
517 return_status = FILE_IGNORE_ALL;
518 else
519 {
520 return_status = file_error (ctx, TRUE, _ ("Cannot read source link\n%s"), src_path);
521 if (return_status == FILE_IGNORE_ALL)
522 ctx->ignore_all = TRUE;
523 if (return_status == FILE_RETRY)
524 goto retry_src_readlink;
525 }
526 goto ret;
527 }
528
529 link_target[len] = '\0';
530
531 if (ctx->stable_symlinks && !(vfs_file_is_local (src_vpath) && vfs_file_is_local (dst_vpath)))
532 {
533 message (D_ERROR, MSG_ERROR,
534 _ ("Cannot make stable symlinks across "
535 "non-local filesystems:\n\nOption Stable Symlinks will be disabled"));
536 ctx->stable_symlinks = FALSE;
537 }
538
539 if (ctx->stable_symlinks && !g_path_is_absolute (link_target))
540 {
541 const char *r;
542
543 r = strrchr (src_path, PATH_SEP);
544 if (r != NULL)
545 {
546 size_t slen;
547 GString *p;
548 vfs_path_t *q;
549
550 slen = r - src_path + 1;
551
552 p = g_string_sized_new (slen + len);
553 g_string_append_len (p, src_path, slen);
554
555 if (g_path_is_absolute (dst_path))
556 q = vfs_path_from_str_flags (dst_path, VPF_NO_CANON);
557 else
558 q = vfs_path_build_filename (p->str, dst_path, (char *) NULL);
559
560 if (vfs_path_tokens_count (q) > 1)
561 {
562 char *s = NULL;
563 vfs_path_t *tmp_vpath1, *tmp_vpath2;
564
565 g_string_append_len (p, link_target, len);
566 tmp_vpath1 = vfs_path_vtokens_get (q, -1, 1);
567 tmp_vpath2 = vfs_path_from_str (p->str);
568 s = diff_two_paths (tmp_vpath1, tmp_vpath2);
569 vfs_path_free (tmp_vpath2, TRUE);
570 vfs_path_free (tmp_vpath1, TRUE);
571 g_strlcpy (link_target, s != NULL ? s : p->str, sizeof (link_target));
572 g_free (s);
573 }
574
575 g_string_free (p, TRUE);
576 vfs_path_free (q, TRUE);
577 }
578 }
579 link_target_vpath = vfs_path_from_str_flags (link_target, VPF_NO_CANON);
580
581 retry_dst_symlink:
582 if (mc_symlink (link_target_vpath, dst_vpath) == 0)
583 {
584
585 return_status = FILE_CONT;
586 goto ret;
587 }
588
589
590
591
592 if (dst_is_symlink && mc_unlink (dst_vpath) == 0
593 && mc_symlink (link_target_vpath, dst_vpath) == 0)
594 {
595
596 return_status = FILE_CONT;
597 goto ret;
598 }
599
600 if (ctx->ignore_all)
601 return_status = FILE_IGNORE_ALL;
602 else
603 {
604 return_status = file_error (ctx, TRUE, _ ("Cannot create target symlink\n%s"), dst_path);
605 if (return_status == FILE_IGNORE_ALL)
606 ctx->ignore_all = TRUE;
607 if (return_status == FILE_RETRY)
608 goto retry_dst_symlink;
609 }
610
611 ret:
612 vfs_path_free (link_target_vpath, TRUE);
613 return return_status;
614 }
615
616
617
618
619
620
621
622
623 static FileProgressStatus
624 do_compute_dir_size (const vfs_path_t *dirname_vpath, dirsize_status_msg_t *dsm, size_t *dir_count,
625 size_t *ret_marked, uintmax_t *ret_total, mc_stat_fn stat_func)
626 {
627 static gint64 timestamp = 0;
628
629 static const gint64 delay = G_USEC_PER_SEC / 25;
630
631 status_msg_t *sm = STATUS_MSG (dsm);
632 int res;
633 struct stat s;
634 DIR *dir;
635 struct vfs_dirent *dirent;
636 FileProgressStatus ret = FILE_CONT;
637
638 (*dir_count)++;
639
640 dir = mc_opendir (dirname_vpath);
641 if (dir == NULL)
642 return ret;
643
644 while (ret == FILE_CONT && (dirent = mc_readdir (dir)) != NULL)
645 {
646 vfs_path_t *tmp_vpath;
647
648 if (DIR_IS_DOT (dirent->d_name) || DIR_IS_DOTDOT (dirent->d_name))
649 continue;
650
651 tmp_vpath = vfs_path_append_new (dirname_vpath, dirent->d_name, (char *) NULL);
652
653 res = stat_func (tmp_vpath, &s);
654 if (res == 0)
655 {
656 if (S_ISDIR (s.st_mode))
657 ret = do_compute_dir_size (tmp_vpath, dsm, dir_count, ret_marked, ret_total,
658 stat_func);
659 else
660 {
661 ret = FILE_CONT;
662
663 (*ret_marked)++;
664 *ret_total += (uintmax_t) s.st_size;
665 }
666
667 if (ret == FILE_CONT && sm->update != NULL && mc_time_elapsed (×tamp, delay))
668 {
669 dsm->dirname_vpath = tmp_vpath;
670 dsm->dir_count = *dir_count;
671 dsm->total_size = *ret_total;
672 ret = sm->update (sm);
673 }
674 }
675
676 vfs_path_free (tmp_vpath, TRUE);
677 }
678
679 mc_closedir (dir);
680 return ret;
681 }
682
683
684
685
686
687
688
689
690
691
692
693 static FileProgressStatus
694 panel_compute_totals (const WPanel *panel, dirsize_status_msg_t *sm, size_t *ret_count,
695 uintmax_t *ret_total, gboolean follow_symlinks)
696 {
697 int i;
698 size_t dir_count = 0;
699 mc_stat_fn stat_func = follow_symlinks ? mc_stat : mc_lstat;
700
701 for (i = 0; i < panel->dir.len; i++)
702 {
703 const file_entry_t *fe = &panel->dir.list[i];
704 const struct stat *s;
705
706 if (fe->f.marked == 0)
707 continue;
708
709 s = &fe->st;
710
711 if (S_ISDIR (s->st_mode) || (follow_symlinks && link_isdir (fe) && fe->f.stale_link == 0))
712 {
713 vfs_path_t *p;
714 FileProgressStatus status;
715
716 p = vfs_path_append_new (panel->cwd_vpath, fe->fname->str, (char *) NULL);
717 status = do_compute_dir_size (p, sm, &dir_count, ret_count, ret_total, stat_func);
718 vfs_path_free (p, TRUE);
719
720 if (status != FILE_CONT)
721 return status;
722 }
723 else
724 {
725 (*ret_count)++;
726 *ret_total += (uintmax_t) s->st_size;
727 }
728 }
729
730 return FILE_CONT;
731 }
732
733
734
735
736 static FileProgressStatus
737 panel_operate_init_totals (const WPanel *panel, const vfs_path_t *source,
738 const struct stat *source_stat, file_op_context_t *ctx,
739 gboolean compute_totals, filegui_dialog_type_t dialog_type)
740 {
741 FileProgressStatus status;
742
743 #ifdef ENABLE_BACKGROUND
744 if (mc_global.we_are_background)
745 return FILE_CONT;
746 #endif
747
748 if (verbose && compute_totals)
749 {
750 dirsize_status_msg_t dsm;
751 gboolean stale_link = FALSE;
752
753 memset (&dsm, 0, sizeof (dsm));
754 dsm.allow_skip = TRUE;
755 status_msg_init (STATUS_MSG (&dsm), _ ("Directory scanning"), 0, dirsize_status_init_cb,
756 dirsize_status_update_cb, dirsize_status_deinit_cb);
757
758 ctx->total_count = 0;
759 ctx->total_bytes = 0;
760
761 if (source == NULL)
762 status = panel_compute_totals (panel, &dsm, &ctx->total_count, &ctx->total_bytes,
763 ctx->follow_links);
764 else if (S_ISDIR (source_stat->st_mode)
765 || (ctx->follow_links
766 && file_is_symlink_to_dir (source, (struct stat *) source_stat, &stale_link)
767 && !stale_link))
768 {
769 size_t dir_count = 0;
770
771 status = do_compute_dir_size (source, &dsm, &dir_count, &ctx->total_count,
772 &ctx->total_bytes, ctx->stat_func);
773 }
774 else
775 {
776 ctx->total_count++;
777 ctx->total_bytes += (uintmax_t) source_stat->st_size;
778 status = FILE_CONT;
779 }
780
781 status_msg_deinit (STATUS_MSG (&dsm));
782
783 ctx->totals_computed = (status == FILE_CONT);
784
785 if (status == FILE_SKIP)
786 status = FILE_CONT;
787 }
788 else
789 {
790 status = FILE_CONT;
791 ctx->total_count = panel->marked;
792 ctx->total_bytes = panel->total;
793 ctx->totals_computed = verbose && dialog_type == FILEGUI_DIALOG_ONE_ITEM;
794 }
795
796
797 file_progress_ui_destroy (ctx);
798
799 file_progress_ui_create (ctx, TRUE, dialog_type);
800
801 return status;
802 }
803
804
805
806 static void
807 progress_update_one (gboolean success, file_op_context_t *ctx, off_t add)
808 {
809 gint64 tv_current;
810 static gint64 tv_start = -1;
811
812 ctx->total_progress_count++;
813 ctx->total_progress_bytes += (uintmax_t) add;
814
815 if (!success)
816 return;
817
818 tv_current = g_get_monotonic_time ();
819
820 if (tv_start < 0)
821 tv_start = tv_current;
822 else if (tv_current - tv_start > FILEOP_UPDATE_INTERVAL_US)
823 {
824 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
825 {
826 file_progress_show_count (ctx);
827 file_progress_show_total (ctx, ctx->total_progress_bytes, tv_current, TRUE);
828 }
829
830 tv_start = tv_current;
831 }
832 }
833
834
835
836 static FileProgressStatus
837 real_warn_same_file (file_op_context_t *ctx, enum OperationMode mode, const char *fmt,
838 const char *a, const char *b)
839 {
840 char *msg;
841 int result = 0;
842 const char *head_msg;
843 int width_a, width_b, width;
844
845 const gint64 t = g_get_monotonic_time ();
846
847 head_msg = mode == Foreground ? MSG_ERROR : _ ("Background process error");
848
849 width_a = str_term_width1 (a);
850 width_b = str_term_width1 (b);
851 width = COLS - 8;
852
853 if (width_a > width)
854 {
855 if (width_b > width)
856 {
857 char *s;
858
859 s = g_strndup (str_trunc (a, width), width);
860 b = str_trunc (b, width);
861 msg = g_strdup_printf (fmt, s, b);
862 g_free (s);
863 }
864 else
865 {
866 a = str_trunc (a, width);
867 msg = g_strdup_printf (fmt, a, b);
868 }
869 }
870 else
871 {
872 if (width_b > width)
873 b = str_trunc (b, width);
874
875 msg = g_strdup_printf (fmt, a, b);
876 }
877
878 result = query_dialog (head_msg, msg, D_ERROR, 2, _ ("&Skip"), _ ("&Abort"));
879 g_free (msg);
880 do_refresh ();
881
882 ctx->pauses += g_get_monotonic_time () - t;
883
884 return (result == 1) ? FILE_ABORT : FILE_SKIP;
885 }
886
887
888
889 static FileProgressStatus
890 warn_same_file (file_op_context_t *ctx, const char *fmt, const char *a, const char *b)
891 {
892 #ifdef ENABLE_BACKGROUND
893 union
894 {
895 void *p;
896 FileProgressStatus (*f) (file_op_context_t *ctx, enum OperationMode, const char *fmt,
897 const char *a, const char *b);
898 } pntr;
899
900 pntr.f = real_warn_same_file;
901
902 if (mc_global.we_are_background)
903 return parent_call (pntr.p, ctx, 3, strlen (fmt), fmt, strlen (a), a, strlen (b), b);
904 #endif
905 return real_warn_same_file (ctx, Foreground, fmt, a, b);
906 }
907
908
909
910 static gboolean
911 check_same_file (file_op_context_t *ctx, const char *a, const struct stat *ast, const char *b,
912 const struct stat *bst, FileProgressStatus *status)
913 {
914 if (ast->st_dev != bst->st_dev || ast->st_ino != bst->st_ino)
915 return FALSE;
916
917 if (S_ISDIR (ast->st_mode))
918 *status = warn_same_file (ctx, _ ("\"%s\"\nand\n\"%s\"\nare the same directory"), a, b);
919 else
920 *status = warn_same_file (ctx, _ ("\"%s\"\nand\n\"%s\"\nare the same file"), a, b);
921
922 return TRUE;
923 }
924
925
926
927
928 static FileProgressStatus
929 real_do_file_error (file_op_context_t *ctx, enum OperationMode mode, gboolean allow_retry,
930 const char *error)
931 {
932 gint64 t = 0;
933 int result;
934 const char *msg;
935
936 if (ctx != NULL)
937 t = g_get_monotonic_time ();
938
939 msg = mode == Foreground ? MSG_ERROR : _ ("Background process error");
940
941 if (allow_retry)
942 result = query_dialog (msg, error, D_ERROR, 4, _ ("&Ignore"), _ ("Ignore a&ll"),
943 _ ("&Retry"), _ ("&Abort"));
944 else
945 result =
946 query_dialog (msg, error, D_ERROR, 3, _ ("&Ignore"), _ ("Ignore a&ll"), _ ("&Abort"));
947
948 if (ctx != NULL)
949 ctx->pauses += g_get_monotonic_time () - t;
950
951 switch (result)
952 {
953 case 0:
954 do_refresh ();
955 return FILE_IGNORE;
956
957 case 1:
958 do_refresh ();
959 return FILE_IGNORE_ALL;
960
961 case 2:
962 if (allow_retry)
963 {
964 do_refresh ();
965 return FILE_RETRY;
966 }
967 MC_FALLTHROUGH;
968
969 case 3:
970 default:
971 return FILE_ABORT;
972 }
973 }
974
975
976
977 static FileProgressStatus
978 real_query_recursive (file_op_context_t *ctx, enum OperationMode mode, const char *s)
979 {
980 if (ctx->recursive_result < RECURSIVE_ALWAYS)
981 {
982 const char *msg;
983 char *text;
984
985 const gint64 t = g_get_monotonic_time ();
986
987 msg = mode == Foreground
988 ? _ ("Directory\n%s\nis not empty.\nDelete it recursively?")
989 : _ ("Background process:\nDirectory\n%s\nis not empty.\nDelete it recursively?");
990
991 text = g_strdup_printf (msg, path_trunc (s, -1));
992
993 if (safe_delete)
994 query_set_sel (1);
995
996 ctx->recursive_result = query_dialog (op_names[OP_DELETE], text, D_ERROR, 5, _ ("&Yes"),
997 _ ("&No"), _ ("A&ll"), _ ("Non&e"), _ ("&Abort"));
998 g_free (text);
999
1000 if (ctx->recursive_result != RECURSIVE_ABORT)
1001 do_refresh ();
1002
1003 ctx->pauses += g_get_monotonic_time () - t;
1004 }
1005
1006 switch (ctx->recursive_result)
1007 {
1008 case RECURSIVE_YES:
1009 case RECURSIVE_ALWAYS:
1010 return FILE_CONT;
1011
1012 case RECURSIVE_NO:
1013 case RECURSIVE_NEVER:
1014 return FILE_SKIP;
1015
1016 case RECURSIVE_ABORT:
1017 default:
1018 return FILE_ABORT;
1019 }
1020 }
1021
1022
1023
1024 #ifdef ENABLE_BACKGROUND
1025 static FileProgressStatus
1026 do_file_error (file_op_context_t *ctx, gboolean allow_retry, const char *str)
1027 {
1028 union
1029 {
1030 void *p;
1031 FileProgressStatus (*f) (file_op_context_t *ctx, enum OperationMode, gboolean,
1032 const char *);
1033 } pntr;
1034
1035 pntr.f = real_do_file_error;
1036
1037 if (mc_global.we_are_background)
1038 return parent_call (pntr.p, ctx, 2, sizeof (allow_retry), allow_retry, strlen (str), str);
1039 else
1040 return real_do_file_error (ctx, Foreground, allow_retry, str);
1041 }
1042
1043
1044
1045 static FileProgressStatus
1046 query_recursive (file_op_context_t *ctx, const char *s)
1047 {
1048 union
1049 {
1050 void *p;
1051 FileProgressStatus (*f) (file_op_context_t *, enum OperationMode, const char *);
1052 } pntr;
1053
1054 pntr.f = real_query_recursive;
1055
1056 if (mc_global.we_are_background)
1057 return parent_call (pntr.p, ctx, 1, strlen (s), s);
1058 else
1059 return real_query_recursive (ctx, Foreground, s);
1060 }
1061
1062
1063
1064 static FileProgressStatus
1065 query_replace (file_op_context_t *ctx, const char *src, struct stat *src_stat, const char *dst,
1066 struct stat *dst_stat)
1067 {
1068 union
1069 {
1070 void *p;
1071 FileProgressStatus (*f) (file_op_context_t *, enum OperationMode, const char *,
1072 struct stat *, const char *, struct stat *);
1073 } pntr;
1074
1075 pntr.f = file_progress_real_query_replace;
1076
1077 if (mc_global.we_are_background)
1078 return parent_call (pntr.p, ctx, 4, strlen (src), src, sizeof (struct stat), src_stat,
1079 strlen (dst), dst, sizeof (struct stat), dst_stat);
1080 else
1081 return file_progress_real_query_replace (ctx, Foreground, src, src_stat, dst, dst_stat);
1082 }
1083
1084 #else
1085
1086
1087 static FileProgressStatus
1088 do_file_error (file_op_context_t *ctx, gboolean allow_retry, const char *str)
1089 {
1090 return real_do_file_error (ctx, Foreground, allow_retry, str);
1091 }
1092
1093
1094
1095 static FileProgressStatus
1096 query_recursive (file_op_context_t *ctx, const char *s)
1097 {
1098 return real_query_recursive (ctx, Foreground, s);
1099 }
1100
1101
1102
1103 static FileProgressStatus
1104 query_replace (file_op_context_t *ctx, const char *src, struct stat *src_stat, const char *dst,
1105 struct stat *dst_stat)
1106 {
1107 return file_progress_real_query_replace (ctx, Foreground, src, src_stat, dst, dst_stat);
1108 }
1109
1110 #endif
1111
1112
1113
1114
1115 static FileProgressStatus
1116 files_error (file_op_context_t *ctx, gboolean allow_retry, const char *format, const char *file1,
1117 const char *file2)
1118 {
1119 char *full_format;
1120 char *nfile1, *nfile2;
1121 char *message;
1122
1123
1124 nfile1 = g_strdup (path_trunc (file1, -1));
1125 nfile2 = g_strdup (path_trunc (file2, -1));
1126 full_format = g_strconcat (format, "\n", unix_error_string (errno), (char *) NULL);
1127 message = g_strdup_printf (full_format, nfile1, nfile2);
1128 g_free (full_format);
1129 g_free (nfile1);
1130 g_free (nfile2);
1131
1132 const FileProgressStatus ret = do_file_error (ctx, allow_retry, message);
1133
1134 g_free (message);
1135
1136 return ret;
1137 }
1138
1139
1140
1141
1142
1143 static void
1144 calc_copy_file_progress (file_op_context_t *ctx, gint64 tv_current, off_t file_part,
1145 off_t file_size)
1146 {
1147 double dt;
1148
1149
1150 rotate_dash (TRUE);
1151
1152
1153 dt = (tv_current - ctx->pauses - ctx->transfer_start) / (double) G_USEC_PER_SEC;
1154
1155 if (file_part == 0)
1156 ctx->eta_secs = 0.0;
1157 else
1158 ctx->eta_secs = ((double) file_size / file_part - 1) * dt;
1159
1160
1161 dt = MAX (1.0, dt);
1162 ctx->bps = (long) (file_part / dt);
1163
1164
1165 if (ctx->total_bytes != 0)
1166 {
1167 dt = (tv_current - ctx->pauses - ctx->total_transfer_start) / (double) G_USEC_PER_SEC;
1168
1169 const uintmax_t copied_bytes = ctx->total_progress_bytes + file_part;
1170 if (copied_bytes == 0)
1171 ctx->total_eta_secs = 0;
1172 else
1173 ctx->total_eta_secs = ((double) ctx->total_bytes / copied_bytes - 1) * dt;
1174
1175 dt = MAX (1.0, dt);
1176 ctx->total_bps = (long) (copied_bytes / dt);
1177 }
1178 }
1179
1180
1181
1182 static gboolean
1183 try_remove_file (file_op_context_t *ctx, const vfs_path_t *vpath, FileProgressStatus *status)
1184 {
1185 while (mc_unlink (vpath) != 0 && errno != ENOENT && !ctx->ignore_all)
1186 {
1187 *status = file_error (ctx, TRUE, _ ("Cannot remove file\n%s"), vfs_path_as_str (vpath));
1188 if (*status == FILE_RETRY)
1189 continue;
1190 if (*status == FILE_IGNORE_ALL)
1191 ctx->ignore_all = TRUE;
1192 return FALSE;
1193 }
1194
1195 return TRUE;
1196 }
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212 static FileProgressStatus
1213 move_file_file (const WPanel *panel, file_op_context_t *ctx, const char *s, const char *d)
1214 {
1215 struct stat src_stat, dst_stat;
1216 FileProgressStatus return_status = FILE_CONT;
1217 gboolean copy_done = FALSE;
1218 gboolean old_ask_overwrite;
1219 vfs_path_t *src_vpath, *dst_vpath;
1220
1221 src_vpath = vfs_path_from_str (s);
1222 dst_vpath = vfs_path_from_str (d);
1223
1224 file_progress_show_source (ctx, src_vpath);
1225 file_progress_show_target (ctx, dst_vpath);
1226
1227
1228 if (file_progress_check_buttons (ctx) == FILE_ABORT)
1229 {
1230 return_status = FILE_ABORT;
1231 goto ret_fast;
1232 }
1233
1234 mc_refresh ();
1235
1236 while (mc_lstat (src_vpath, &src_stat) != 0)
1237 {
1238
1239 if (ctx->ignore_all)
1240 return_status = FILE_IGNORE_ALL;
1241 else
1242 {
1243 return_status = file_error (ctx, TRUE, _ ("Cannot stat file\n%s"), s);
1244 if (return_status == FILE_IGNORE_ALL)
1245 ctx->ignore_all = TRUE;
1246 }
1247
1248 if (return_status != FILE_RETRY)
1249 goto ret;
1250 }
1251
1252 if (mc_lstat (dst_vpath, &dst_stat) == 0)
1253 {
1254 if (check_same_file (ctx, s, &src_stat, d, &dst_stat, &return_status))
1255 goto ret;
1256
1257 if (S_ISDIR (dst_stat.st_mode))
1258 {
1259 message (D_ERROR, MSG_ERROR, _ ("Cannot overwrite directory \"%s\""), d);
1260 do_refresh ();
1261 return_status = FILE_SKIP;
1262 goto ret;
1263 }
1264
1265 if (confirm_overwrite)
1266 {
1267 return_status = query_replace (ctx, s, &src_stat, d, &dst_stat);
1268 if (return_status != FILE_CONT)
1269 goto ret;
1270 }
1271
1272 }
1273
1274 if (!ctx->do_append)
1275 {
1276 if (S_ISLNK (src_stat.st_mode) && ctx->stable_symlinks)
1277 {
1278 return_status = make_symlink (ctx, src_vpath, dst_vpath);
1279 if (return_status == FILE_CONT)
1280 {
1281 if (ctx->preserve)
1282 {
1283 mc_timesbuf_t times;
1284
1285 vfs_get_timesbuf_from_stat (&src_stat, ×);
1286 mc_utime (dst_vpath, ×);
1287 }
1288 goto retry_src_remove;
1289 }
1290 goto ret;
1291 }
1292
1293 if (mc_rename (src_vpath, dst_vpath) == 0)
1294 goto ret;
1295 }
1296 #if 0
1297
1298
1299
1300
1301 else
1302 errno = EXDEV;
1303
1304 if (errno != EXDEV)
1305 {
1306 if (ctx->ignore_all)
1307 return_status = FILE_IGNORE_ALL;
1308 else
1309 {
1310 return_status = files_error (ctx, TRUE, _("Cannot move file\n%s\nto\n%s"), s, d);
1311 if (return_status == FILE_IGNORE_ALL)
1312 ctx->ignore_all = TRUE;
1313 if (return_status == FILE_RETRY)
1314 goto retry_rename;
1315 }
1316
1317 goto ret;
1318 }
1319 #endif
1320
1321
1322 if (panel != NULL)
1323 {
1324
1325
1326 return_status = panel_operate_init_totals (panel, src_vpath, &src_stat, ctx, TRUE,
1327 FILEGUI_DIALOG_ONE_ITEM);
1328 if (return_status != FILE_CONT)
1329 goto ret_fast;
1330 }
1331
1332 old_ask_overwrite = ctx->ask_overwrite;
1333 ctx->ask_overwrite = FALSE;
1334 return_status = copy_file_file (ctx, s, d);
1335 ctx->ask_overwrite = old_ask_overwrite;
1336 if (return_status != FILE_CONT)
1337 goto ret;
1338
1339 copy_done = TRUE;
1340
1341
1342
1343 if (panel == NULL)
1344 {
1345 file_progress_show_source (ctx, NULL);
1346 if (verbose)
1347 file_progress_show (ctx, 0, 0, "", FALSE);
1348
1349 return_status = file_progress_check_buttons (ctx);
1350 if (return_status != FILE_CONT)
1351 goto ret_fast;
1352 }
1353
1354 mc_refresh ();
1355
1356 retry_src_remove:
1357 if (!try_remove_file (ctx, src_vpath, &return_status) && panel == NULL)
1358 goto ret_fast;
1359
1360 ret:
1361 if (return_status != FILE_ABORT)
1362 {
1363
1364 if (!copy_done)
1365 progress_update_one (TRUE, ctx, src_stat.st_size);
1366 return_status = file_progress_check_buttons (ctx);
1367 }
1368 ret_fast:
1369 vfs_path_free (src_vpath, TRUE);
1370 vfs_path_free (dst_vpath, TRUE);
1371
1372 return return_status;
1373 }
1374
1375
1376
1377
1378
1379
1380
1381 static FileProgressStatus
1382 erase_file (file_op_context_t *ctx, const vfs_path_t *vpath)
1383 {
1384 struct stat buf;
1385 FileProgressStatus return_status;
1386
1387
1388 if (file_progress_show_deleting (ctx, vpath, &ctx->total_progress_count))
1389 {
1390 file_progress_show_count (ctx);
1391 if (file_progress_check_buttons (ctx) == FILE_ABORT)
1392 return FILE_ABORT;
1393
1394 mc_refresh ();
1395 }
1396
1397 if (ctx->total_progress_count != 0 && mc_lstat (vpath, &buf) != 0)
1398 {
1399
1400 buf.st_size = 0;
1401 }
1402
1403 if (!try_remove_file (ctx, vpath, &return_status) && return_status == FILE_ABORT)
1404 return FILE_ABORT;
1405
1406 if (ctx->total_progress_count == 0)
1407 return FILE_CONT;
1408
1409 return file_progress_check_buttons (ctx);
1410 }
1411
1412
1413
1414 static FileProgressStatus
1415 try_erase_dir (file_op_context_t *ctx, const vfs_path_t *vpath)
1416 {
1417 FileProgressStatus return_status = FILE_CONT;
1418
1419 while (mc_rmdir (vpath) != 0 && !ctx->ignore_all)
1420 {
1421 return_status =
1422 file_error (ctx, TRUE, _ ("Cannot remove directory\n%s"), vfs_path_as_str (vpath));
1423 if (return_status == FILE_IGNORE_ALL)
1424 ctx->ignore_all = TRUE;
1425 if (return_status != FILE_RETRY)
1426 break;
1427 }
1428
1429 return return_status;
1430 }
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440 static FileProgressStatus
1441 recursive_erase (file_op_context_t *ctx, const vfs_path_t *vpath)
1442 {
1443 struct vfs_dirent *next;
1444 DIR *reading;
1445 FileProgressStatus return_status = FILE_CONT;
1446
1447 reading = mc_opendir (vpath);
1448 if (reading == NULL)
1449 return FILE_RETRY;
1450
1451 while ((next = mc_readdir (reading)) && return_status != FILE_ABORT)
1452 {
1453 vfs_path_t *tmp_vpath;
1454 struct stat buf;
1455
1456 if (DIR_IS_DOT (next->d_name) || DIR_IS_DOTDOT (next->d_name))
1457 continue;
1458
1459 tmp_vpath = vfs_path_append_new (vpath, next->d_name, (char *) NULL);
1460 if (mc_lstat (tmp_vpath, &buf) != 0)
1461 {
1462 const gboolean vanished = (errno == ENOENT);
1463
1464 vfs_path_free (tmp_vpath, TRUE);
1465
1466
1467 if (vanished)
1468 continue;
1469
1470 mc_closedir (reading);
1471 return FILE_RETRY;
1472 }
1473 if (S_ISDIR (buf.st_mode))
1474 return_status = recursive_erase (ctx, tmp_vpath);
1475 else
1476 return_status = erase_file (ctx, tmp_vpath);
1477 vfs_path_free (tmp_vpath, TRUE);
1478 }
1479 mc_closedir (reading);
1480
1481 if (return_status == FILE_ABORT)
1482 return FILE_ABORT;
1483
1484 file_progress_show_deleting (ctx, vpath, NULL);
1485 file_progress_show_count (ctx);
1486 if (file_progress_check_buttons (ctx) == FILE_ABORT)
1487 return FILE_ABORT;
1488
1489 mc_refresh ();
1490
1491 return try_erase_dir (ctx, vpath);
1492 }
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510 static int
1511 check_dir_is_empty (file_op_context_t *ctx, const vfs_path_t *vpath, FileProgressStatus *error)
1512 {
1513 DIR *dir;
1514 struct vfs_dirent *d;
1515 int i = 1;
1516
1517 while ((dir = mc_opendir (vpath)) == NULL)
1518 {
1519 if (ctx->ignore_all)
1520 *error = FILE_IGNORE_ALL;
1521 else
1522 {
1523 const FileProgressStatus status = file_error (
1524 ctx, TRUE, _ ("Cannot enter into directory\n%s"), vfs_path_as_str (vpath));
1525
1526 if (status == FILE_RETRY)
1527 continue;
1528 if (status == FILE_IGNORE_ALL)
1529 ctx->ignore_all = TRUE;
1530
1531 *error = status;
1532 }
1533
1534 return (-1);
1535 }
1536
1537 for (d = mc_readdir (dir); d != NULL; d = mc_readdir (dir))
1538 if (!DIR_IS_DOT (d->d_name) && !DIR_IS_DOTDOT (d->d_name))
1539 {
1540 i = 0;
1541 break;
1542 }
1543
1544 mc_closedir (dir);
1545 *error = FILE_CONT;
1546 return i;
1547 }
1548
1549
1550
1551 static FileProgressStatus
1552 erase_dir_iff_empty (file_op_context_t *ctx, const vfs_path_t *vpath)
1553 {
1554 FileProgressStatus error = FILE_CONT;
1555
1556 file_progress_show_deleting (ctx, vpath, NULL);
1557 file_progress_show_count (ctx);
1558 if (file_progress_check_buttons (ctx) == FILE_ABORT)
1559 return FILE_ABORT;
1560
1561 mc_refresh ();
1562
1563 const int res = check_dir_is_empty (ctx, vpath, &error);
1564
1565 if (res == -1)
1566 return error;
1567
1568 if (res != 1)
1569 return FILE_CONT;
1570
1571
1572 return try_erase_dir (ctx, vpath);
1573 }
1574
1575
1576
1577 static void
1578 erase_dir_after_copy (file_op_context_t *ctx, const vfs_path_t *vpath, FileProgressStatus *status)
1579 {
1580 if (ctx->erase_at_end && erase_list != NULL)
1581 {
1582
1583 ctx->total_progress_count = ctx->prev_total_progress_count;
1584
1585 while (!g_queue_is_empty (erase_list) && *status != FILE_ABORT)
1586 {
1587 link_t *lp;
1588
1589 lp = (link_t *) g_queue_pop_head (erase_list);
1590
1591 if (S_ISDIR (lp->st_mode))
1592 *status = erase_dir_iff_empty (ctx, lp->src_vpath);
1593 else
1594 *status = erase_file (ctx, lp->src_vpath);
1595
1596 free_link (lp);
1597 }
1598
1599
1600 ctx->prev_total_progress_count = ctx->total_progress_count;
1601 }
1602
1603 erase_dir_iff_empty (ctx, vpath);
1604 }
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620 static FileProgressStatus
1621 do_move_dir_dir (const WPanel *panel, file_op_context_t *ctx, const char *s, const char *d)
1622 {
1623 struct stat src_stat, dst_stat;
1624 FileProgressStatus return_status = FILE_CONT;
1625 gboolean move_over = FALSE;
1626 gboolean dstat_ok;
1627 vfs_path_t *src_vpath, *dst_vpath;
1628
1629 src_vpath = vfs_path_from_str (s);
1630 dst_vpath = vfs_path_from_str (d);
1631
1632 file_progress_show_source (ctx, src_vpath);
1633 file_progress_show_target (ctx, dst_vpath);
1634
1635
1636 if (panel != NULL && file_progress_check_buttons (ctx) == FILE_ABORT)
1637 {
1638 return_status = FILE_ABORT;
1639 goto ret_fast;
1640 }
1641
1642 mc_refresh ();
1643
1644 mc_stat (src_vpath, &src_stat);
1645
1646 dstat_ok = (mc_stat (dst_vpath, &dst_stat) == 0);
1647
1648 if (dstat_ok && check_same_file (ctx, s, &src_stat, d, &dst_stat, &return_status))
1649 goto ret_fast;
1650
1651 if (!dstat_ok)
1652 ;
1653 else if (!ctx->dive_into_subdirs)
1654 move_over = TRUE;
1655 else
1656 {
1657 vfs_path_t *tmp;
1658
1659 tmp = dst_vpath;
1660 dst_vpath = vfs_path_append_new (dst_vpath, x_basename (s), (char *) NULL);
1661 vfs_path_free (tmp, TRUE);
1662 }
1663
1664 d = vfs_path_as_str (dst_vpath);
1665
1666
1667 retry_dst_stat:
1668 if (mc_stat (dst_vpath, &dst_stat) == 0)
1669 {
1670 if (move_over)
1671 {
1672 if (panel != NULL)
1673 {
1674
1675
1676 return_status = panel_operate_init_totals (panel, src_vpath, &src_stat, ctx, TRUE,
1677 FILEGUI_DIALOG_MULTI_ITEM);
1678 if (return_status != FILE_CONT)
1679 goto ret;
1680 }
1681
1682 return_status = copy_dir_dir (ctx, s, d, FALSE, TRUE, TRUE, NULL);
1683
1684 if (return_status != FILE_CONT)
1685 goto ret;
1686 goto oktoret;
1687 }
1688 else if (ctx->ignore_all)
1689 return_status = FILE_IGNORE_ALL;
1690 else
1691 {
1692 if (S_ISDIR (dst_stat.st_mode))
1693 return_status = file_error (ctx, TRUE, _ ("Cannot overwrite directory\n%s"), d);
1694 else
1695 return_status = file_error (ctx, TRUE, _ ("Cannot overwrite file\n%s"), d);
1696 if (return_status == FILE_IGNORE_ALL)
1697 ctx->ignore_all = TRUE;
1698 if (return_status == FILE_RETRY)
1699 goto retry_dst_stat;
1700 }
1701
1702 goto ret_fast;
1703 }
1704
1705 retry_rename:
1706 if (mc_rename (src_vpath, dst_vpath) == 0)
1707 {
1708 return_status = FILE_CONT;
1709 goto ret;
1710 }
1711
1712 if (errno != EXDEV)
1713 {
1714 if (!ctx->ignore_all)
1715 {
1716 return_status = files_error (ctx, TRUE, _ ("Cannot move directory\n%s\nto\n%s"), s, d);
1717 if (return_status == FILE_IGNORE_ALL)
1718 ctx->ignore_all = TRUE;
1719 if (return_status == FILE_RETRY)
1720 goto retry_rename;
1721 }
1722 goto ret;
1723 }
1724
1725
1726 if (panel != NULL)
1727 {
1728
1729
1730 return_status = panel_operate_init_totals (panel, src_vpath, &src_stat, ctx, TRUE,
1731 FILEGUI_DIALOG_MULTI_ITEM);
1732 if (return_status != FILE_CONT)
1733 goto ret;
1734 }
1735
1736 return_status = copy_dir_dir (ctx, s, d, FALSE, FALSE, TRUE, NULL);
1737
1738 if (return_status != FILE_CONT)
1739 goto ret;
1740
1741 oktoret:
1742
1743
1744 if (panel == NULL)
1745 {
1746 file_progress_show_source (ctx, NULL);
1747 file_progress_show_target (ctx, NULL);
1748 if (verbose)
1749 file_progress_show (ctx, 0, 0, "", FALSE);
1750
1751 return_status = file_progress_check_buttons (ctx);
1752 if (return_status != FILE_CONT)
1753 goto ret;
1754 }
1755
1756 mc_refresh ();
1757
1758 erase_dir_after_copy (ctx, src_vpath, &return_status);
1759
1760 ret:
1761 erase_list = free_erase_list (erase_list);
1762 ret_fast:
1763 vfs_path_free (src_vpath, TRUE);
1764 vfs_path_free (dst_vpath, TRUE);
1765 return return_status;
1766 }
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777 static const char *
1778 panel_get_file (const WPanel *panel)
1779 {
1780 const file_entry_t *fe;
1781
1782 if (get_current_type () == view_tree)
1783 {
1784 WTree *tree;
1785 const vfs_path_t *selected_name;
1786
1787 tree = (WTree *) get_panel_widget (get_current_index ());
1788 selected_name = tree_selected_name (tree);
1789 return vfs_path_as_str (selected_name);
1790 }
1791
1792 if (panel->marked != 0)
1793 {
1794 int i;
1795
1796 for (i = 0; i < panel->dir.len; i++)
1797 if (panel->dir.list[i].f.marked != 0)
1798 return panel->dir.list[i].fname->str;
1799 }
1800
1801 fe = panel_current_entry (panel);
1802
1803 return (fe == NULL ? NULL : fe->fname->str);
1804 }
1805
1806
1807
1808 static const char *
1809 check_single_entry (const WPanel *panel, gboolean force_single, struct stat *src_stat)
1810 {
1811 const char *source;
1812 gboolean ok;
1813
1814 if (force_single)
1815 {
1816 const file_entry_t *fe;
1817
1818 fe = panel_current_entry (panel);
1819 source = fe == NULL ? NULL : fe->fname->str;
1820 }
1821 else
1822 source = panel_get_file (panel);
1823
1824 if (source == NULL)
1825 return NULL;
1826
1827 ok = !DIR_IS_DOTDOT (source);
1828
1829 if (!ok)
1830 message (D_ERROR, MSG_ERROR, _ ("Cannot operate on \"..\"!"));
1831 else
1832 {
1833 vfs_path_t *source_vpath;
1834
1835 source_vpath = vfs_path_from_str (source);
1836
1837
1838 ok = mc_lstat (source_vpath, src_stat) == 0;
1839 if (!ok)
1840 {
1841 file_error_message (_ ("Cannot stat\n%s"), source);
1842
1843
1844 if (!panel->is_panelized)
1845 {
1846 panel_update_flags_t flags = UP_RELOAD;
1847
1848
1849 if (get_other_type () == view_listing && other_panel->is_panelized)
1850 flags |= UP_ONLY_CURRENT;
1851
1852 update_panels (flags, UP_KEEPSEL);
1853 }
1854 }
1855
1856 vfs_path_free (source_vpath, TRUE);
1857 }
1858
1859 return ok ? source : NULL;
1860 }
1861
1862
1863
1864
1865
1866
1867
1868 static char *
1869 panel_operate_generate_prompt (const WPanel *panel, FileOperation operation,
1870 const struct stat *src_stat)
1871 {
1872 char *sp;
1873 char *format_string;
1874 const char *cp;
1875
1876 static gboolean i18n_flag = FALSE;
1877 if (!i18n_flag)
1878 {
1879 size_t i;
1880
1881 for (i = G_N_ELEMENTS (op_names1); i-- != 0;)
1882 op_names1[i] = Q_ (op_names1[i]);
1883
1884 #ifdef ENABLE_NLS
1885 for (i = G_N_ELEMENTS (prompt_parts); i-- != 0;)
1886 prompt_parts[i] = _ (prompt_parts[i]);
1887
1888 one_format = _ (one_format);
1889 many_format = _ (many_format);
1890 #endif
1891 i18n_flag = TRUE;
1892 }
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915 cp = (src_stat != NULL ? one_format : many_format);
1916
1917
1918 format_string = str_replace_all (cp, "%o", op_names1[(int) operation]);
1919
1920
1921 cp = operation == OP_DELETE ? "\n" : " ";
1922 sp = format_string;
1923 format_string = str_replace_all (sp, "%n", cp);
1924 g_free (sp);
1925
1926
1927 if (src_stat != NULL)
1928 cp = S_ISDIR (src_stat->st_mode) ? prompt_parts[2] : prompt_parts[0];
1929 else if (panel->marked == panel->dirs_marked)
1930 cp = prompt_parts[3];
1931 else
1932 cp = panel->dirs_marked != 0 ? prompt_parts[4] : prompt_parts[1];
1933
1934 sp = format_string;
1935 format_string = str_replace_all (sp, "%f", cp);
1936 g_free (sp);
1937
1938
1939 cp = operation == OP_DELETE ? "?" : prompt_parts[5];
1940 sp = format_string;
1941 format_string = str_replace_all (sp, "%m", cp);
1942 g_free (sp);
1943
1944 return format_string;
1945 }
1946
1947
1948
1949 static char *
1950 do_confirm_copy_move (const WPanel *panel, gboolean force_single, const char *source,
1951 struct stat *src_stat, file_op_context_t *ctx, gboolean *do_bg)
1952 {
1953 const char *tmp_dest_dir;
1954 char *dest_dir;
1955 char *format;
1956 char *ret;
1957
1958
1959 if (force_single)
1960 tmp_dest_dir = source;
1961 else if (get_other_type () == view_listing)
1962 tmp_dest_dir = vfs_path_as_str (other_panel->cwd_vpath);
1963 else
1964 tmp_dest_dir = vfs_path_as_str (panel->cwd_vpath);
1965
1966
1967
1968
1969
1970
1971 if (!force_single && tmp_dest_dir != NULL && tmp_dest_dir[0] != '\0'
1972 && !IS_PATH_SEP (tmp_dest_dir[strlen (tmp_dest_dir) - 1]))
1973 {
1974
1975 dest_dir = g_strconcat (tmp_dest_dir, PATH_SEP_STR, (char *) NULL);
1976 }
1977 else
1978 {
1979
1980 dest_dir = g_strdup (tmp_dest_dir);
1981 }
1982
1983 if (dest_dir == NULL)
1984 return NULL;
1985
1986 if (source == NULL)
1987 src_stat = NULL;
1988
1989
1990 format = panel_operate_generate_prompt (panel, ctx->operation, src_stat);
1991
1992 ret =
1993 file_mask_dialog (ctx, source != NULL, format,
1994 source != NULL ? source : (const void *) &panel->marked, dest_dir, do_bg);
1995
1996 g_free (format);
1997 g_free (dest_dir);
1998
1999 return ret;
2000 }
2001
2002
2003
2004 static gboolean
2005 do_confirm_erase (const WPanel *panel, const char *source, struct stat *src_stat)
2006 {
2007 int i;
2008 char *format;
2009 char fmd_buf[BUF_MEDIUM];
2010
2011 if (source == NULL)
2012 src_stat = NULL;
2013
2014
2015 format = panel_operate_generate_prompt (panel, OP_DELETE, src_stat);
2016
2017 if (source == NULL)
2018 g_snprintf (fmd_buf, sizeof (fmd_buf), format, panel->marked);
2019 else
2020 {
2021 const int fmd_xlen = 64;
2022
2023 i = fmd_xlen - str_term_width1 (format) - 4;
2024 g_snprintf (fmd_buf, sizeof (fmd_buf), format, str_trunc (source, i));
2025 }
2026
2027 g_free (format);
2028
2029 if (safe_delete)
2030 query_set_sel (1);
2031
2032 i = query_dialog (op_names[OP_DELETE], fmd_buf, D_ERROR, 2, _ ("&Yes"), _ ("&No"));
2033
2034 return (i == 0);
2035 }
2036
2037
2038
2039 static FileProgressStatus
2040 operate_single_file (const WPanel *panel, file_op_context_t *ctx, const char *src,
2041 struct stat *src_stat, const char *dest, filegui_dialog_type_t dialog_type)
2042 {
2043 FileProgressStatus value;
2044 vfs_path_t *src_vpath;
2045 gboolean is_file;
2046
2047 if (g_path_is_absolute (src))
2048 src_vpath = vfs_path_from_str (src);
2049 else
2050 src_vpath = vfs_path_append_new (panel->cwd_vpath, src, (char *) NULL);
2051
2052 is_file = !S_ISDIR (src_stat->st_mode);
2053
2054 if (is_file)
2055 {
2056 gboolean is_link;
2057
2058 is_link = file_is_symlink_to_dir (src_vpath, src_stat, NULL);
2059 is_file = !(is_link && ctx->follow_links);
2060 }
2061
2062 if (ctx->operation == OP_DELETE)
2063 {
2064 value = panel_operate_init_totals (panel, src_vpath, src_stat, ctx, !is_file, dialog_type);
2065 if (value == FILE_CONT)
2066 {
2067 if (is_file)
2068 value = erase_file (ctx, src_vpath);
2069 else
2070 value = erase_dir (ctx, src_vpath);
2071 }
2072 }
2073 else
2074 {
2075 char *temp;
2076
2077 src = vfs_path_as_str (src_vpath);
2078
2079 temp = build_dest (ctx, src, dest, &value);
2080 if (temp != NULL)
2081 {
2082 dest = temp;
2083
2084 switch (ctx->operation)
2085 {
2086 case OP_COPY:
2087
2088 ctx->stat_func (src_vpath, src_stat);
2089
2090 value = panel_operate_init_totals (panel, src_vpath, src_stat, ctx, !is_file,
2091 dialog_type);
2092 if (value == FILE_CONT)
2093 {
2094 is_file = !S_ISDIR (src_stat->st_mode);
2095
2096 if (is_file)
2097 {
2098 gboolean is_link;
2099
2100 is_link = file_is_symlink_to_dir (src_vpath, src_stat, NULL);
2101 is_file = !(is_link && ctx->follow_links);
2102 }
2103
2104 if (is_file)
2105 value = copy_file_file (ctx, src, dest);
2106 else
2107 value = copy_dir_dir (ctx, src, dest, TRUE, FALSE, FALSE, NULL);
2108 }
2109 break;
2110
2111 case OP_MOVE:
2112 #ifdef ENABLE_BACKGROUND
2113 if (!mc_global.we_are_background)
2114 #endif
2115
2116 file_progress_ui_create (ctx, TRUE, FILEGUI_DIALOG_ONE_ITEM);
2117
2118 if (is_file)
2119 value = move_file_file (panel, ctx, src, dest);
2120 else
2121 value = do_move_dir_dir (panel, ctx, src, dest);
2122 break;
2123
2124 default:
2125
2126 abort ();
2127 }
2128
2129 g_free (temp);
2130 }
2131 }
2132
2133 vfs_path_free (src_vpath, TRUE);
2134
2135 return value;
2136 }
2137
2138
2139
2140 static FileProgressStatus
2141 operate_one_file (const WPanel *panel, file_op_context_t *ctx, const char *src,
2142 struct stat *src_stat, const char *dest)
2143 {
2144 FileProgressStatus value = FILE_CONT;
2145 vfs_path_t *src_vpath;
2146 gboolean is_file;
2147
2148 if (g_path_is_absolute (src))
2149 src_vpath = vfs_path_from_str (src);
2150 else
2151 src_vpath = vfs_path_append_new (panel->cwd_vpath, src, (char *) NULL);
2152
2153 is_file = !S_ISDIR (src_stat->st_mode);
2154
2155 if (ctx->operation == OP_DELETE)
2156 {
2157 if (is_file)
2158 value = erase_file (ctx, src_vpath);
2159 else
2160 value = erase_dir (ctx, src_vpath);
2161 }
2162 else
2163 {
2164 char *temp;
2165
2166 src = vfs_path_as_str (src_vpath);
2167
2168 temp = build_dest (ctx, src, dest, &value);
2169 if (temp != NULL)
2170 {
2171 dest = temp;
2172
2173 switch (ctx->operation)
2174 {
2175 case OP_COPY:
2176
2177 ctx->stat_func (src_vpath, src_stat);
2178 is_file = !S_ISDIR (src_stat->st_mode);
2179
2180 if (is_file)
2181 value = copy_file_file (ctx, src, dest);
2182 else
2183 value = copy_dir_dir (ctx, src, dest, TRUE, FALSE, FALSE, NULL);
2184 dest_dirs = free_linklist (dest_dirs);
2185 break;
2186
2187 case OP_MOVE:
2188 if (is_file)
2189 value = move_file_file (NULL, ctx, src, dest);
2190 else
2191 value = do_move_dir_dir (NULL, ctx, src, dest);
2192 break;
2193
2194 default:
2195
2196 abort ();
2197 }
2198
2199 g_free (temp);
2200 }
2201 }
2202
2203 vfs_path_free (src_vpath, TRUE);
2204
2205 return value;
2206 }
2207
2208
2209
2210 #ifdef ENABLE_BACKGROUND
2211 static int
2212 end_bg_process (file_op_context_t *ctx, enum OperationMode mode)
2213 {
2214 int pid = ctx->pid;
2215
2216 (void) mode;
2217 ctx->pid = 0;
2218
2219 unregister_task_with_pid (pid);
2220
2221 return 1;
2222 }
2223 #endif
2224
2225
2226
2227
2228
2229
2230
2231
2232 static inline gboolean
2233 attrs_ignore_error (const int e)
2234 {
2235 return (e == ENOTSUP || e == EOPNOTSUPP || e == ENOSYS || e == EINVAL || e == ENOTTY
2236 || e == ELOOP || e == ENXIO);
2237 }
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251 gboolean
2252 file_is_symlink_to_dir (const vfs_path_t *vpath, struct stat *st, gboolean *stale_link)
2253 {
2254 struct stat st2;
2255 gboolean stale = FALSE;
2256 gboolean res = FALSE;
2257
2258 if (st == NULL)
2259 {
2260 st = &st2;
2261
2262 if (mc_lstat (vpath, st) != 0)
2263 goto ret;
2264 }
2265
2266 if (S_ISLNK (st->st_mode))
2267 {
2268 struct stat st3;
2269
2270 stale = (mc_stat (vpath, &st3) != 0);
2271
2272 if (!stale)
2273 res = (S_ISDIR (st3.st_mode) != 0);
2274 }
2275
2276 ret:
2277 if (stale_link != NULL)
2278 *stale_link = stale;
2279
2280 return res;
2281 }
2282
2283
2284
2285 FileProgressStatus
2286 copy_file_file (file_op_context_t *ctx, const char *src_path, const char *dst_path)
2287 {
2288 uid_t src_uid = (uid_t) (-1);
2289 gid_t src_gid = (gid_t) (-1);
2290
2291 int src_desc, dest_desc = -1;
2292 mode_t src_mode = 0;
2293 struct stat src_stat, dst_stat;
2294 mc_timesbuf_t times;
2295 unsigned long attrs = 0;
2296 gboolean attrs_ok = copymove_persistent_ext2_attr;
2297 gboolean dst_exists = FALSE, appending = FALSE;
2298 off_t file_size = -1;
2299 FileProgressStatus return_status, temp_status;
2300 dest_status_t dst_status = DEST_NONE;
2301 int open_flags;
2302 vfs_path_t *src_vpath = NULL, *dst_vpath = NULL;
2303 char *buf = NULL;
2304
2305
2306
2307
2308 if (ctx->do_reget < 0)
2309 ctx->do_reget = 0;
2310
2311 return_status = FILE_RETRY;
2312
2313 dst_vpath = vfs_path_from_str (dst_path);
2314 src_vpath = vfs_path_from_str (src_path);
2315
2316 file_progress_show_source (ctx, src_vpath);
2317 file_progress_show_target (ctx, dst_vpath);
2318
2319 if (file_progress_check_buttons (ctx) == FILE_ABORT)
2320 {
2321 return_status = FILE_ABORT;
2322 goto ret_fast;
2323 }
2324
2325 mc_refresh ();
2326
2327 while (mc_stat (dst_vpath, &dst_stat) == 0)
2328 {
2329 if (S_ISDIR (dst_stat.st_mode))
2330 {
2331 if (ctx->ignore_all)
2332 return_status = FILE_IGNORE_ALL;
2333 else
2334 {
2335 return_status =
2336 file_error (ctx, TRUE, _ ("Cannot overwrite directory\n%s"), dst_path);
2337 if (return_status == FILE_IGNORE_ALL)
2338 ctx->ignore_all = TRUE;
2339 if (return_status == FILE_RETRY)
2340 continue;
2341 }
2342 goto ret_fast;
2343 }
2344
2345 dst_exists = TRUE;
2346 break;
2347 }
2348
2349 while ((*ctx->stat_func) (src_vpath, &src_stat) != 0)
2350 {
2351 if (ctx->ignore_all)
2352 return_status = FILE_IGNORE_ALL;
2353 else
2354 {
2355 return_status = file_error (ctx, TRUE, _ ("Cannot stat source file\n%s"), src_path);
2356 if (return_status == FILE_IGNORE_ALL)
2357 ctx->ignore_all = TRUE;
2358 }
2359
2360 if (return_status != FILE_RETRY)
2361 {
2362
2363 progress_update_one (FALSE, ctx, 0);
2364 goto ret_fast;
2365 }
2366 }
2367
2368
2369 src_mode = src_stat.st_mode;
2370 src_uid = src_stat.st_uid;
2371 src_gid = src_stat.st_gid;
2372 file_size = src_stat.st_size;
2373
2374 while (attrs_ok && mc_fgetflags (src_vpath, &attrs) != 0)
2375 {
2376 attrs_ok = FALSE;
2377
2378
2379 if (attrs_ignore_error (errno))
2380 return_status = FILE_CONT;
2381 else if (ctx->ignore_all)
2382 return_status = FILE_IGNORE_ALL;
2383 else
2384 {
2385 return_status = file_error (
2386 ctx, TRUE, _ ("Cannot get ext2 attributes of source file\n%s"), src_path);
2387 if (return_status == FILE_IGNORE_ALL)
2388 ctx->ignore_all = TRUE;
2389 if (return_status == FILE_ABORT)
2390 goto ret_fast;
2391 }
2392
2393 if (return_status != FILE_RETRY)
2394 break;
2395
2396
2397 attrs_ok = TRUE;
2398 }
2399
2400 if (dst_exists)
2401 {
2402
2403 if (check_same_file (ctx, src_path, &src_stat, dst_path, &dst_stat, &return_status))
2404 goto ret_fast;
2405
2406
2407 if (ctx->ask_overwrite)
2408 {
2409 ctx->do_reget = 0;
2410 return_status = query_replace (ctx, src_path, &src_stat, dst_path, &dst_stat);
2411 if (return_status != FILE_CONT)
2412 goto ret_fast;
2413 }
2414 }
2415
2416 vfs_get_timesbuf_from_stat (&src_stat, ×);
2417
2418 if (!ctx->do_append)
2419 {
2420
2421 if (!ctx->follow_links)
2422 {
2423 switch (check_hardlinks (ctx, src_vpath, &src_stat, dst_vpath, &ctx->ignore_all))
2424 {
2425 case HARDLINK_OK:
2426
2427 return_status = FILE_CONT;
2428 goto ret_fast;
2429
2430 case HARDLINK_ABORT:
2431 return_status = FILE_ABORT;
2432 goto ret_fast;
2433
2434 default:
2435 break;
2436 }
2437 }
2438
2439 if (S_ISLNK (src_stat.st_mode))
2440 {
2441 return_status = make_symlink (ctx, src_vpath, dst_vpath);
2442 if (return_status == FILE_CONT && ctx->preserve)
2443 {
2444 mc_utime (dst_vpath, ×);
2445
2446 while (attrs_ok && mc_fsetflags (dst_vpath, attrs) != 0 && !ctx->ignore_all)
2447 {
2448 attrs_ok = FALSE;
2449
2450
2451 if (attrs_ignore_error (errno))
2452 return_status = FILE_CONT;
2453 else if (return_status == FILE_IGNORE_ALL)
2454 ctx->ignore_all = TRUE;
2455 else
2456 return_status = file_error (
2457 ctx, TRUE, _ ("Cannot set ext2 attributes of target file\n%s"),
2458 dst_path);
2459
2460 if (return_status != FILE_RETRY)
2461 break;
2462
2463
2464 attrs_ok = TRUE;
2465 }
2466 }
2467 goto ret_fast;
2468 }
2469
2470 if (S_ISCHR (src_stat.st_mode) || S_ISBLK (src_stat.st_mode) || S_ISFIFO (src_stat.st_mode)
2471 || S_ISNAM (src_stat.st_mode) || S_ISSOCK (src_stat.st_mode))
2472 {
2473 dev_t rdev = 0;
2474
2475 #ifdef HAVE_STRUCT_STAT_ST_RDEV
2476 rdev = src_stat.st_rdev;
2477 #endif
2478
2479 while (mc_mknod (dst_vpath, src_stat.st_mode & ctx->umask_kill, rdev) < 0
2480 && !ctx->ignore_all)
2481 {
2482 return_status =
2483 file_error (ctx, TRUE, _ ("Cannot create special file\n%s"), dst_path);
2484 if (return_status == FILE_RETRY)
2485 continue;
2486 if (return_status == FILE_IGNORE_ALL)
2487 ctx->ignore_all = TRUE;
2488 goto ret_fast;
2489 }
2490
2491
2492 while (ctx->preserve_uidgid
2493 && mc_chown (dst_vpath, src_stat.st_uid, src_stat.st_gid) != 0
2494 && !ctx->ignore_all)
2495 {
2496 temp_status = file_error (ctx, TRUE, _ ("Cannot chown target file\n%s"), dst_path);
2497 if (temp_status == FILE_IGNORE)
2498 break;
2499 if (temp_status == FILE_IGNORE_ALL)
2500 ctx->ignore_all = TRUE;
2501 if (temp_status != FILE_RETRY)
2502 {
2503 return_status = temp_status;
2504 goto ret_fast;
2505 }
2506 }
2507
2508 while (ctx->preserve && mc_chmod (dst_vpath, src_stat.st_mode & ctx->umask_kill) != 0
2509 && !ctx->ignore_all)
2510 {
2511 temp_status = file_error (ctx, TRUE, _ ("Cannot chmod target file\n%s"), dst_path);
2512 if (temp_status == FILE_IGNORE)
2513 break;
2514 if (temp_status == FILE_IGNORE_ALL)
2515 ctx->ignore_all = TRUE;
2516 if (temp_status != FILE_RETRY)
2517 {
2518 return_status = temp_status;
2519 goto ret_fast;
2520 }
2521 }
2522
2523 while (attrs_ok && mc_fsetflags (dst_vpath, attrs) != 0 && !ctx->ignore_all)
2524 {
2525 attrs_ok = FALSE;
2526
2527
2528 if (attrs_ignore_error (errno))
2529 break;
2530
2531 temp_status = file_error (
2532 ctx, TRUE, _ ("Cannot set ext2 attributes of target file\n%s"), dst_path);
2533 if (temp_status == FILE_IGNORE)
2534 break;
2535 if (temp_status == FILE_IGNORE_ALL)
2536 ctx->ignore_all = TRUE;
2537 if (temp_status != FILE_RETRY)
2538 {
2539 return_status = temp_status;
2540 goto ret_fast;
2541 }
2542
2543
2544 attrs_ok = TRUE;
2545 }
2546
2547 return_status = FILE_CONT;
2548 mc_utime (dst_vpath, ×);
2549 goto ret_fast;
2550 }
2551 }
2552
2553 ctx->transfer_start = g_get_monotonic_time ();
2554
2555 while ((src_desc = mc_open (src_vpath, O_RDONLY | O_LINEAR)) < 0)
2556 {
2557 if (ctx->ignore_all)
2558 return_status = FILE_IGNORE_ALL;
2559 else
2560 {
2561 return_status = file_error (ctx, TRUE, _ ("Cannot open source file\n%s"), src_path);
2562 if (return_status == FILE_RETRY)
2563 continue;
2564 if (return_status == FILE_IGNORE_ALL)
2565 ctx->ignore_all = TRUE;
2566 ctx->do_append = FALSE;
2567 }
2568 goto ret;
2569 }
2570
2571 if (ctx->do_reget != 0 && mc_lseek (src_desc, ctx->do_reget, SEEK_SET) != ctx->do_reget)
2572 {
2573 message (D_ERROR, _ ("Warning"), _ ("Reget failed, about to overwrite file"));
2574 ctx->do_reget = 0;
2575 ctx->do_append = FALSE;
2576 }
2577
2578 while (mc_fstat (src_desc, &src_stat) != 0)
2579 {
2580 if (ctx->ignore_all)
2581 return_status = FILE_IGNORE_ALL;
2582 else
2583 {
2584 return_status = file_error (ctx, TRUE, _ ("Cannot fstat source file\n%s"), src_path);
2585 if (return_status == FILE_RETRY)
2586 continue;
2587 if (return_status == FILE_IGNORE_ALL)
2588 ctx->ignore_all = TRUE;
2589 ctx->do_append = FALSE;
2590 }
2591 goto ret;
2592 }
2593
2594
2595 src_mode = src_stat.st_mode;
2596 src_uid = src_stat.st_uid;
2597 src_gid = src_stat.st_gid;
2598 file_size = src_stat.st_size;
2599
2600 #ifdef HAVE_FILE_CLONING_BY_PATH
2601
2602
2603
2604
2605
2606 if (mc_global.vfs.file_cloning && !dst_exists)
2607 {
2608
2609 if (vfs_clone_file_by_path (dst_vpath, src_vpath, ctx->preserve_uidgid) == 0)
2610 {
2611 dst_status = DEST_FULL;
2612 return_status = FILE_CONT;
2613 goto ret;
2614 }
2615 }
2616 #endif
2617
2618 open_flags = O_WRONLY;
2619 if (!dst_exists)
2620 open_flags |= O_CREAT | O_EXCL;
2621 else if (ctx->do_append)
2622 #ifdef HAVE_FILE_CLONING_BY_RANGE
2623
2624
2625
2626 open_flags |= mc_global.vfs.file_cloning ? 0 : O_APPEND;
2627 #else
2628 open_flags |= O_APPEND;
2629 #endif
2630 else
2631 open_flags |= O_CREAT | O_TRUNC;
2632
2633 #ifdef HAVE_FILE_CLONING_BY_RANGE
2634 open_dest:
2635 #endif
2636 while ((dest_desc = mc_open (dst_vpath, open_flags, src_mode)) < 0)
2637 {
2638 if (errno != EEXIST)
2639 {
2640 if (ctx->ignore_all)
2641 return_status = FILE_IGNORE_ALL;
2642 else
2643 {
2644 return_status =
2645 file_error (ctx, TRUE, _ ("Cannot create target file\n%s"), dst_path);
2646 if (return_status == FILE_RETRY)
2647 continue;
2648 if (return_status == FILE_IGNORE_ALL)
2649 ctx->ignore_all = TRUE;
2650 ctx->do_append = FALSE;
2651 }
2652 }
2653 goto ret;
2654 }
2655
2656
2657 dst_status = DEST_SHORT_QUERY;
2658
2659 appending = ctx->do_append;
2660 ctx->do_append = FALSE;
2661
2662 #ifdef HAVE_FILE_CLONING_BY_RANGE
2663
2664 if (mc_global.vfs.file_cloning && (open_flags & O_APPEND) == 0)
2665 {
2666 if ((appending ? mc_lseek (dest_desc, 0, SEEK_END) >= 0 : TRUE)
2667 && vfs_clone_file (dest_desc, src_desc) == 0)
2668 {
2669 dst_status = DEST_FULL;
2670 return_status = FILE_CONT;
2671 goto ret;
2672 }
2673 if (appending && (open_flags & O_APPEND) == 0)
2674 {
2675
2676 ctx->do_append = TRUE;
2677 mc_close (dest_desc);
2678 dst_status = DEST_NONE;
2679 open_flags |= O_APPEND;
2680 goto open_dest;
2681 }
2682 }
2683 #endif
2684
2685
2686 while (mc_fstat (dest_desc, &dst_stat) != 0)
2687 {
2688 if (ctx->ignore_all)
2689 return_status = FILE_IGNORE_ALL;
2690 else
2691 {
2692 return_status = file_error (ctx, TRUE, _ ("Cannot fstat target file\n%s"), dst_path);
2693 if (return_status == FILE_RETRY)
2694 continue;
2695 if (return_status == FILE_IGNORE_ALL)
2696 ctx->ignore_all = TRUE;
2697 }
2698 goto ret;
2699 }
2700
2701
2702 while (mc_global.vfs.preallocate_space
2703 && vfs_preallocate (dest_desc, file_size, appending ? dst_stat.st_size : 0) != 0)
2704 {
2705 if (ctx->ignore_all)
2706 {
2707
2708 return_status = FILE_CONT;
2709 break;
2710 }
2711
2712 return_status =
2713 file_error (ctx, TRUE, _ ("Cannot preallocate space for target file\n%s"), dst_path);
2714
2715 if (return_status == FILE_IGNORE_ALL)
2716 ctx->ignore_all = TRUE;
2717
2718 if (ctx->ignore_all || return_status == FILE_IGNORE)
2719 {
2720
2721 return_status = FILE_CONT;
2722 break;
2723 }
2724
2725 if (return_status == FILE_ABORT)
2726 {
2727 mc_close (dest_desc);
2728 dest_desc = -1;
2729 mc_unlink (dst_vpath);
2730 dst_status = DEST_NONE;
2731 goto ret;
2732 }
2733
2734
2735 }
2736
2737 ctx->eta_secs = 0.0;
2738 ctx->bps = 0;
2739
2740 if (verbose)
2741 {
2742 if (ctx->total_bps == 0 || (file_size / ctx->total_bps) > FILEOP_UPDATE_INTERVAL)
2743 file_progress_show (ctx, 0, file_size, "", TRUE);
2744 else
2745 file_progress_show (ctx, 1, 1, "", TRUE);
2746 }
2747
2748 return_status = file_progress_check_buttons (ctx);
2749 mc_refresh ();
2750
2751 if (return_status == FILE_CONT)
2752 {
2753 off_t file_part = 0;
2754 gint64 tv_last_update = ctx->transfer_start;
2755 gint64 tv_last_input = 0;
2756 gboolean is_first_time = TRUE;
2757
2758 const size_t bufsize = io_blksize (dst_stat);
2759 buf = g_malloc (bufsize);
2760
2761 while (TRUE)
2762 {
2763 ssize_t n_read = -1;
2764
2765
2766 if (mc_ctl (src_desc, VFS_CTL_IS_NOTREADY, 0) == 0)
2767 while ((n_read = mc_read (src_desc, buf, bufsize)) < 0 && !ctx->ignore_all)
2768 {
2769 return_status =
2770 file_error (ctx, TRUE, _ ("Cannot read source file\n%s"), src_path);
2771 if (return_status == FILE_RETRY)
2772 continue;
2773 if (return_status == FILE_IGNORE_ALL)
2774 ctx->ignore_all = TRUE;
2775 goto ret;
2776 }
2777
2778 if (n_read == 0)
2779 break;
2780
2781 const gint64 tv_current = g_get_monotonic_time ();
2782
2783 if (n_read > 0)
2784 {
2785 ssize_t n_written;
2786 char *t = buf;
2787
2788 file_part += n_read;
2789
2790 tv_last_input = tv_current;
2791
2792
2793 while ((n_written = mc_write (dest_desc, t, (size_t) n_read)) < n_read)
2794 {
2795 gboolean write_errno_nospace;
2796
2797 if (n_written > 0)
2798 {
2799 n_read -= n_written;
2800 t += n_written;
2801 continue;
2802 }
2803
2804 write_errno_nospace = (n_written < 0 && errno == ENOSPC);
2805
2806 if (ctx->ignore_all)
2807 return_status = FILE_IGNORE_ALL;
2808 else
2809 return_status =
2810 file_error (ctx, TRUE, _ ("Cannot write target file\n%s"), dst_path);
2811
2812 if (return_status == FILE_IGNORE)
2813 {
2814 if (write_errno_nospace)
2815 goto ret;
2816 break;
2817 }
2818 if (return_status == FILE_IGNORE_ALL)
2819 {
2820 ctx->ignore_all = TRUE;
2821 if (write_errno_nospace)
2822 goto ret;
2823 }
2824 if (return_status != FILE_RETRY)
2825 goto ret;
2826 }
2827 }
2828
2829 ctx->progress_bytes = file_part + ctx->do_reget;
2830
2831 const gint64 usecs = tv_current - tv_last_update;
2832
2833 if (is_first_time || usecs > FILEOP_UPDATE_INTERVAL_US)
2834 {
2835 calc_copy_file_progress (ctx, tv_current, file_part, file_size - ctx->do_reget);
2836 tv_last_update = tv_current;
2837 }
2838
2839 is_first_time = FALSE;
2840
2841 if (verbose)
2842 {
2843 const gint64 total_usecs = tv_current - ctx->total_transfer_start;
2844 const gboolean force_update = total_usecs > FILEOP_UPDATE_INTERVAL_US;
2845
2846 const gint64 update_usecs = tv_current - tv_last_input;
2847 const char *stalled_msg =
2848 update_usecs > FILEOP_STALLING_INTERVAL_US ? _ ("(stalled)") : "";
2849
2850 file_progress_show (ctx, ctx->progress_bytes, file_size, stalled_msg, force_update);
2851 if (ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
2852 {
2853 file_progress_show_count (ctx);
2854 file_progress_show_total (ctx, ctx->total_progress_bytes + ctx->progress_bytes,
2855 tv_current, force_update);
2856 }
2857
2858 mc_refresh ();
2859 }
2860
2861 return_status = file_progress_check_buttons (ctx);
2862 if (return_status != FILE_CONT)
2863 {
2864 int query_res;
2865
2866 const gint64 t1 = g_get_monotonic_time ();
2867 query_res =
2868 query_dialog (Q_ ("DialogTitle|Copy"), _ ("Incomplete file was retrieved"),
2869 D_ERROR, 3, _ ("&Delete"), _ ("&Keep"), _ ("&Continue copy"));
2870 const gint64 t2 = g_get_monotonic_time ();
2871 ctx->pauses += t2 - t1;
2872
2873
2874 calc_copy_file_progress (ctx, t2, file_part, file_size - ctx->do_reget);
2875
2876 switch (query_res)
2877 {
2878 case 0:
2879
2880 dst_status = DEST_SHORT_DELETE;
2881 goto ret;
2882
2883 case 1:
2884
2885 dst_status = DEST_SHORT_KEEP;
2886 goto ret;
2887
2888 default:
2889
2890 break;
2891 }
2892 }
2893 }
2894
2895
2896 dst_status = DEST_FULL;
2897 }
2898
2899 ret:
2900 g_free (buf);
2901
2902 rotate_dash (FALSE);
2903 while (src_desc != -1 && mc_close (src_desc) < 0 && !ctx->ignore_all)
2904 {
2905 temp_status = file_error (ctx, TRUE, _ ("Cannot close source file\n%s"), src_path);
2906 if (temp_status == FILE_RETRY)
2907 continue;
2908 if (temp_status == FILE_ABORT)
2909 return_status = temp_status;
2910 if (temp_status == FILE_IGNORE_ALL)
2911 ctx->ignore_all = TRUE;
2912 break;
2913 }
2914
2915 while (dest_desc != -1 && mc_close (dest_desc) < 0 && !ctx->ignore_all)
2916 {
2917 temp_status = file_error (ctx, TRUE, _ ("Cannot close target file\n%s"), dst_path);
2918 if (temp_status == FILE_RETRY)
2919 continue;
2920 if (temp_status == FILE_IGNORE_ALL)
2921 ctx->ignore_all = TRUE;
2922 return_status = temp_status;
2923 break;
2924 }
2925
2926 if (dst_status == DEST_SHORT_QUERY)
2927 {
2928
2929 if (query_dialog (Q_ ("DialogTitle|Copy"), _ ("Incomplete file was retrieved"), D_ERROR, 2,
2930 _ ("&Delete"), _ ("&Keep"))
2931 == 0)
2932 dst_status = DEST_SHORT_DELETE;
2933 else
2934 dst_status = DEST_SHORT_KEEP;
2935 }
2936
2937 if (dst_status == DEST_SHORT_DELETE)
2938 mc_unlink (dst_vpath);
2939 else if (dst_status == DEST_FULL && !appending)
2940 {
2941
2942
2943 while (ctx->preserve_uidgid && mc_chown (dst_vpath, src_uid, src_gid) != 0
2944 && !ctx->ignore_all)
2945 {
2946 temp_status = file_error (ctx, TRUE, _ ("Cannot chown target file\n%s"), dst_path);
2947 if (temp_status == FILE_ABORT)
2948 {
2949 return_status = FILE_ABORT;
2950 goto ret_fast;
2951 }
2952 if (temp_status == FILE_RETRY)
2953 continue;
2954 if (temp_status == FILE_IGNORE_ALL)
2955 {
2956 ctx->ignore_all = TRUE;
2957 return_status = FILE_CONT;
2958 }
2959 if (temp_status == FILE_IGNORE)
2960 return_status = FILE_CONT;
2961 break;
2962 }
2963
2964 while (ctx->preserve && mc_chmod (dst_vpath, (src_mode & ctx->umask_kill)) != 0
2965 && !ctx->ignore_all)
2966 {
2967 temp_status = file_error (ctx, TRUE, _ ("Cannot chmod target file\n%s"), dst_path);
2968 if (temp_status == FILE_ABORT)
2969 {
2970 return_status = FILE_ABORT;
2971 goto ret_fast;
2972 }
2973 if (temp_status == FILE_RETRY)
2974 continue;
2975 if (temp_status == FILE_IGNORE_ALL)
2976 {
2977 ctx->ignore_all = TRUE;
2978 return_status = FILE_CONT;
2979 }
2980 if (temp_status == FILE_IGNORE)
2981 return_status = FILE_CONT;
2982 break;
2983 }
2984
2985 if (!ctx->preserve && !dst_exists)
2986 {
2987 src_mode = umask (-1);
2988 umask (src_mode);
2989 src_mode = 0100666 & ~src_mode;
2990 mc_chmod (dst_vpath, (src_mode & ctx->umask_kill));
2991 }
2992
2993 while (attrs_ok && mc_fsetflags (dst_vpath, attrs) != 0 && !ctx->ignore_all)
2994 {
2995 attrs_ok = FALSE;
2996
2997
2998 if (attrs_ignore_error (errno))
2999 {
3000 return_status = FILE_CONT;
3001 break;
3002 }
3003
3004 temp_status = file_error (
3005 ctx, TRUE, _ ("Cannot set ext2 attributes for target file\n%s"), dst_path);
3006 if (temp_status == FILE_ABORT)
3007 {
3008 return_status = FILE_ABORT;
3009 goto ret_fast;
3010 }
3011 if (temp_status == FILE_RETRY)
3012 {
3013 attrs_ok = TRUE;
3014 continue;
3015 }
3016 if (temp_status == FILE_IGNORE_ALL)
3017 {
3018 ctx->ignore_all = TRUE;
3019 return_status = FILE_CONT;
3020 }
3021 if (temp_status == FILE_IGNORE)
3022 return_status = FILE_CONT;
3023 break;
3024 }
3025 }
3026
3027
3028 if (dst_status == DEST_FULL || dst_status == DEST_SHORT_KEEP)
3029 mc_utime (dst_vpath, ×);
3030
3031 progress_update_one (return_status == FILE_CONT, ctx, file_size);
3032 if (return_status == FILE_CONT)
3033 return_status = file_progress_check_buttons (ctx);
3034
3035 ret_fast:
3036 vfs_path_free (src_vpath, TRUE);
3037 vfs_path_free (dst_vpath, TRUE);
3038 ctx->do_reget = -1;
3039 return return_status;
3040 }
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050 FileProgressStatus
3051 copy_dir_dir (file_op_context_t *ctx, const char *s, const char *d, gboolean toplevel,
3052 gboolean move_over, gboolean do_delete, GSList *parent_dirs)
3053 {
3054 struct vfs_dirent *next;
3055 struct stat dst_stat, src_stat;
3056 unsigned long attrs = 0;
3057 gboolean attrs_ok = copymove_persistent_ext2_attr;
3058 DIR *reading;
3059 FileProgressStatus return_status = FILE_CONT;
3060 link_t *lp;
3061 vfs_path_t *src_vpath, *dst_vpath;
3062 gboolean do_mkdir = TRUE;
3063
3064 src_vpath = vfs_path_from_str (s);
3065 dst_vpath = vfs_path_from_str (d);
3066
3067
3068
3069 retry_src_stat:
3070 while ((*ctx->stat_func) (src_vpath, &src_stat) != 0)
3071 {
3072 if (ctx->ignore_all)
3073 return_status = FILE_IGNORE_ALL;
3074 else
3075 {
3076 return_status = file_error (ctx, TRUE, _ ("Cannot stat source directory\n%s"), s);
3077 if (return_status == FILE_IGNORE_ALL)
3078 ctx->ignore_all = TRUE;
3079 }
3080
3081 if (return_status != FILE_RETRY)
3082 goto ret_fast;
3083 }
3084
3085 while (attrs_ok && mc_fgetflags (src_vpath, &attrs) != 0)
3086 {
3087 attrs_ok = FALSE;
3088
3089
3090 if (attrs_ignore_error (errno))
3091 return_status = FILE_CONT;
3092 else if (ctx->ignore_all)
3093 return_status = FILE_IGNORE_ALL;
3094 else
3095 {
3096 return_status =
3097 file_error (ctx, TRUE, _ ("Cannot get ext2 attributes of source directory\n%s"), s);
3098 if (return_status == FILE_IGNORE_ALL)
3099 ctx->ignore_all = TRUE;
3100 if (return_status == FILE_ABORT)
3101 goto ret_fast;
3102 }
3103
3104 if (return_status != FILE_RETRY)
3105 break;
3106
3107
3108 attrs_ok = TRUE;
3109 }
3110
3111 if (is_in_linklist (dest_dirs, src_vpath, &src_stat) != NULL)
3112 {
3113
3114
3115
3116 return_status = FILE_CONT;
3117 goto ret_fast;
3118 }
3119
3120
3121
3122
3123 if (ctx->preserve)
3124 {
3125 switch (check_hardlinks (ctx, src_vpath, &src_stat, dst_vpath, &ctx->ignore_all))
3126 {
3127 case HARDLINK_OK:
3128
3129 goto ret_fast;
3130
3131 case HARDLINK_ABORT:
3132 return_status = FILE_ABORT;
3133 goto ret_fast;
3134
3135 default:
3136 break;
3137 }
3138 }
3139
3140 if (!S_ISDIR (src_stat.st_mode))
3141 {
3142 if (ctx->ignore_all)
3143 return_status = FILE_IGNORE_ALL;
3144 else
3145 {
3146 return_status = file_error (ctx, TRUE, _ ("Source \"%s\" is not a directory\n%s"), s);
3147 if (return_status == FILE_RETRY)
3148 goto retry_src_stat;
3149 if (return_status == FILE_IGNORE_ALL)
3150 ctx->ignore_all = TRUE;
3151 }
3152 goto ret_fast;
3153 }
3154
3155 if (is_in_linklist (parent_dirs, src_vpath, &src_stat) != NULL)
3156 {
3157
3158 message (D_ERROR, MSG_ERROR, _ ("Cannot copy cyclic symbolic link\n\"%s\""), s);
3159 return_status = FILE_SKIP;
3160 goto ret_fast;
3161 }
3162
3163 lp = g_new0 (link_t, 1);
3164 lp->vfs = vfs_path_get_last_path_vfs (src_vpath);
3165 lp->ino = src_stat.st_ino;
3166 lp->dev = src_stat.st_dev;
3167 parent_dirs = g_slist_prepend (parent_dirs, lp);
3168
3169 retry_dst_stat:
3170
3171 if (mc_stat (dst_vpath, &dst_stat) != 0)
3172 {
3173
3174 if (move_over && mc_rename (src_vpath, dst_vpath) == 0)
3175 {
3176 return_status = FILE_CONT;
3177 goto ret;
3178 }
3179 }
3180 else
3181 {
3182
3183
3184
3185
3186
3187
3188
3189
3190 if (!S_ISDIR (dst_stat.st_mode))
3191 {
3192 if (ctx->ignore_all)
3193 return_status = FILE_IGNORE_ALL;
3194 else
3195 {
3196 return_status =
3197 file_error (ctx, TRUE, _ ("Destination\n%s\nmust be a directory\n%s"), d);
3198 if (return_status == FILE_IGNORE_ALL)
3199 ctx->ignore_all = TRUE;
3200 if (return_status == FILE_RETRY)
3201 goto retry_dst_stat;
3202 }
3203 goto ret;
3204 }
3205
3206 if (toplevel && ctx->dive_into_subdirs)
3207 {
3208 vfs_path_t *tmp;
3209
3210 tmp = dst_vpath;
3211 dst_vpath = vfs_path_append_new (dst_vpath, x_basename (s), (char *) NULL);
3212 vfs_path_free (tmp, TRUE);
3213 }
3214 else
3215 do_mkdir = FALSE;
3216 }
3217
3218 d = vfs_path_as_str (dst_vpath);
3219
3220 if (do_mkdir)
3221 {
3222 while (mc_mkdir (dst_vpath, (src_stat.st_mode & ctx->umask_kill) | S_IRWXU) != 0)
3223 {
3224 if (ctx->ignore_all)
3225 return_status = FILE_IGNORE_ALL;
3226 else
3227 {
3228 return_status = file_error (ctx, TRUE, _ ("Cannot create target directory\n%s"), d);
3229 if (return_status == FILE_IGNORE_ALL)
3230 ctx->ignore_all = TRUE;
3231 }
3232 if (return_status != FILE_RETRY)
3233 goto ret;
3234 }
3235
3236 lp = g_new0 (link_t, 1);
3237 mc_stat (dst_vpath, &dst_stat);
3238 lp->vfs = vfs_path_get_last_path_vfs (dst_vpath);
3239 lp->ino = dst_stat.st_ino;
3240 lp->dev = dst_stat.st_dev;
3241 dest_dirs = g_slist_prepend (dest_dirs, lp);
3242 }
3243
3244 if (ctx->preserve_uidgid)
3245 {
3246 while (mc_chown (dst_vpath, src_stat.st_uid, src_stat.st_gid) != 0)
3247 {
3248 if (ctx->ignore_all)
3249 return_status = FILE_IGNORE_ALL;
3250 else
3251 {
3252 return_status = file_error (ctx, TRUE, _ ("Cannot chown target directory\n%s"), d);
3253 if (return_status == FILE_IGNORE_ALL)
3254 ctx->ignore_all = TRUE;
3255 }
3256 if (return_status != FILE_RETRY)
3257 goto ret;
3258 }
3259 }
3260
3261
3262 reading = mc_opendir (src_vpath);
3263 if (reading == NULL)
3264 goto ret;
3265
3266 while ((next = mc_readdir (reading)) && return_status != FILE_ABORT)
3267 {
3268 char *path;
3269 vfs_path_t *tmp_vpath;
3270
3271
3272
3273
3274 if (DIR_IS_DOT (next->d_name) || DIR_IS_DOTDOT (next->d_name))
3275 continue;
3276
3277
3278 path = mc_build_filename (s, next->d_name, (char *) NULL);
3279 tmp_vpath = vfs_path_from_str (path);
3280
3281 (*ctx->stat_func) (tmp_vpath, &dst_stat);
3282 if (S_ISDIR (dst_stat.st_mode))
3283 {
3284 char *mdpath;
3285
3286 mdpath = mc_build_filename (d, next->d_name, (char *) NULL);
3287
3288
3289
3290
3291
3292
3293 return_status = copy_dir_dir (ctx, path, mdpath, FALSE, FALSE, do_delete, parent_dirs);
3294 g_free (mdpath);
3295 }
3296 else
3297 {
3298 char *dest_file;
3299
3300 dest_file = mc_build_filename (d, x_basename (path), (char *) NULL);
3301 return_status = copy_file_file (ctx, path, dest_file);
3302 g_free (dest_file);
3303 }
3304
3305 g_free (path);
3306
3307 if (do_delete && return_status == FILE_CONT)
3308 {
3309 if (ctx->erase_at_end)
3310 {
3311 if (erase_list == NULL)
3312 erase_list = g_queue_new ();
3313
3314 lp = g_new0 (link_t, 1);
3315 lp->src_vpath = tmp_vpath;
3316 lp->st_mode = dst_stat.st_mode;
3317 g_queue_push_tail (erase_list, lp);
3318 tmp_vpath = NULL;
3319 }
3320 else if (S_ISDIR (dst_stat.st_mode))
3321 return_status = erase_dir_iff_empty (ctx, tmp_vpath);
3322 else
3323 return_status = erase_file (ctx, tmp_vpath);
3324 }
3325 vfs_path_free (tmp_vpath, TRUE);
3326 }
3327 mc_closedir (reading);
3328
3329 if (ctx->preserve)
3330 {
3331 mc_timesbuf_t times;
3332
3333 mc_chmod (dst_vpath, src_stat.st_mode & ctx->umask_kill);
3334
3335 if (attrs_ok)
3336 mc_fsetflags (dst_vpath, attrs);
3337
3338 vfs_get_timesbuf_from_stat (&src_stat, ×);
3339 mc_utime (dst_vpath, ×);
3340 }
3341 else
3342 {
3343 src_stat.st_mode = umask (-1);
3344 umask (src_stat.st_mode);
3345 src_stat.st_mode = 0100777 & ~src_stat.st_mode;
3346 mc_chmod (dst_vpath, src_stat.st_mode & ctx->umask_kill);
3347 }
3348
3349 ret:
3350 free_link (parent_dirs->data);
3351 g_slist_free_1 (parent_dirs);
3352 ret_fast:
3353 vfs_path_free (src_vpath, TRUE);
3354 vfs_path_free (dst_vpath, TRUE);
3355 return return_status;
3356 }
3357
3358
3359
3360
3361
3362
3363 FileProgressStatus
3364 move_dir_dir (file_op_context_t *ctx, const char *s, const char *d)
3365 {
3366 return do_move_dir_dir (NULL, ctx, s, d);
3367 }
3368
3369
3370
3371
3372
3373
3374 FileProgressStatus
3375 erase_dir (file_op_context_t *ctx, const vfs_path_t *vpath)
3376 {
3377 FileProgressStatus error = FILE_CONT;
3378
3379 file_progress_show_deleting (ctx, vpath, NULL);
3380 file_progress_show_count (ctx);
3381 if (file_progress_check_buttons (ctx) == FILE_ABORT)
3382 return FILE_ABORT;
3383
3384 mc_refresh ();
3385
3386 const int res = check_dir_is_empty (ctx, vpath, &error);
3387
3388 if (res == -1)
3389 return error;
3390
3391 if (res == 0)
3392 {
3393
3394 error = query_recursive (ctx, vfs_path_as_str (vpath));
3395 if (error == FILE_CONT)
3396 error = recursive_erase (ctx, vpath);
3397 return error;
3398 }
3399
3400 return try_erase_dir (ctx, vpath);
3401 }
3402
3403
3404
3405
3406
3407
3408 void
3409 dirsize_status_init_cb (status_msg_t *sm)
3410 {
3411 dirsize_status_msg_t *dsm = (dirsize_status_msg_t *) sm;
3412 WGroup *gd = GROUP (sm->dlg);
3413 Widget *wd = WIDGET (sm->dlg);
3414 WRect r = wd->rect;
3415
3416 const char *b1_name = _ ("&Abort");
3417 const char *b2_name = _ ("&Skip");
3418 int b_width, ui_width;
3419
3420 b_width = str_term_width1 (b1_name) + 4;
3421 if (dsm->allow_skip)
3422 b_width += str_term_width1 (b2_name) + 4 + 1;
3423
3424 ui_width = MAX (COLS / 2, b_width + 6);
3425 dsm->dirname = label_new (2, 3, NULL);
3426 group_add_widget (gd, dsm->dirname);
3427 dsm->count_size = label_new (3, 3, NULL);
3428 group_add_widget (gd, dsm->count_size);
3429 group_add_widget (gd, hline_new (4, -1, -1));
3430
3431 dsm->abort_button = WIDGET (button_new (5, 3, FILE_ABORT, NORMAL_BUTTON, b1_name, NULL));
3432 group_add_widget (gd, dsm->abort_button);
3433 if (dsm->allow_skip)
3434 {
3435 dsm->skip_button = WIDGET (button_new (5, 3, FILE_SKIP, NORMAL_BUTTON, b2_name, NULL));
3436 group_add_widget (gd, dsm->skip_button);
3437 widget_select (dsm->skip_button);
3438 }
3439
3440 r.lines = 8;
3441 r.cols = ui_width;
3442 widget_set_size_rect (wd, &r);
3443 dirsize_status_locate_buttons (dsm);
3444 }
3445
3446
3447
3448 int
3449 dirsize_status_update_cb (status_msg_t *sm)
3450 {
3451 dirsize_status_msg_t *dsm = (dirsize_status_msg_t *) sm;
3452 Widget *wd = WIDGET (sm->dlg);
3453 WRect r = wd->rect;
3454
3455
3456 label_set_textv (dsm->count_size, _ ("Directories: %zu, total size: %s"), dsm->dir_count,
3457 size_trunc_sep (dsm->total_size, panels_options.kilobyte_si));
3458
3459
3460 if (WIDGET (dsm->count_size)->rect.cols + 6 > r.cols)
3461 {
3462 r.cols = WIDGET (dsm->count_size)->rect.cols + 6;
3463 widget_set_size_rect (wd, &r);
3464 dirsize_status_locate_buttons (dsm);
3465 widget_draw (wd);
3466
3467 }
3468
3469
3470 label_set_text (dsm->dirname,
3471 str_trunc (vfs_path_as_str (dsm->dirname_vpath), wd->rect.cols - 6));
3472
3473 switch (status_msg_common_update (sm))
3474 {
3475 case B_CANCEL:
3476 case FILE_ABORT:
3477 return FILE_ABORT;
3478 case FILE_SKIP:
3479 return FILE_SKIP;
3480 default:
3481 return FILE_CONT;
3482 }
3483 }
3484
3485
3486
3487 void
3488 dirsize_status_deinit_cb (status_msg_t *sm)
3489 {
3490 (void) sm;
3491
3492
3493 if (get_other_type () == view_listing)
3494 other_panel->dirty = TRUE;
3495 }
3496
3497
3498
3499
3500
3501
3502
3503
3504 FileProgressStatus
3505 compute_dir_size (const vfs_path_t *dirname_vpath, dirsize_status_msg_t *sm, size_t *ret_dir_count,
3506 size_t *ret_marked_count, uintmax_t *ret_total, gboolean follow_symlinks)
3507 {
3508 return do_compute_dir_size (dirname_vpath, sm, ret_dir_count, ret_marked_count, ret_total,
3509 follow_symlinks ? mc_stat : mc_lstat);
3510 }
3511
3512
3513
3514
3515
3516
3517
3518
3519
3520
3521
3522
3523
3524
3525
3526 gboolean
3527 panel_operate (void *source_panel, FileOperation operation, gboolean force_single)
3528 {
3529 WPanel *panel = PANEL (source_panel);
3530 const gboolean single_entry =
3531 force_single || (panel->marked <= 1) || (get_current_type () == view_tree);
3532
3533 const char *source = NULL;
3534 char *dest = NULL;
3535 vfs_path_t *dest_vpath = NULL;
3536 vfs_path_t *save_cwd = NULL, *save_dest = NULL;
3537 struct stat src_stat;
3538 gboolean ret_val = TRUE;
3539 int i;
3540 FileProgressStatus value;
3541 file_op_context_t *ctx;
3542 filegui_dialog_type_t dialog_type = FILEGUI_DIALOG_ONE_ITEM;
3543
3544 gboolean do_bg = FALSE;
3545
3546 linklist = free_linklist (linklist);
3547 dest_dirs = free_linklist (dest_dirs);
3548
3549 save_cwds_stat ();
3550
3551 if (single_entry)
3552 {
3553 source = check_single_entry (panel, force_single, &src_stat);
3554
3555 if (source == NULL)
3556 return FALSE;
3557 }
3558
3559 ctx = file_op_context_new (operation);
3560
3561
3562 if (operation != OP_DELETE)
3563 {
3564 dest = do_confirm_copy_move (panel, force_single, source, &src_stat, ctx, &do_bg);
3565 if (dest == NULL)
3566 {
3567 ret_val = FALSE;
3568 goto ret_fast;
3569 }
3570
3571 dest_vpath = vfs_path_from_str (dest);
3572 }
3573 else if (confirm_delete && !do_confirm_erase (panel, source, &src_stat))
3574 {
3575 ret_val = FALSE;
3576 goto ret_fast;
3577 }
3578
3579 ctx->total_transfer_start = g_get_monotonic_time ();
3580
3581 #ifdef ENABLE_BACKGROUND
3582
3583 if (do_bg)
3584 {
3585 int v;
3586
3587 v = do_background (ctx,
3588 g_strconcat (op_names[operation], ": ",
3589 vfs_path_as_str (panel->cwd_vpath), (char *) NULL));
3590 if (v == -1)
3591 message (D_ERROR, MSG_ERROR, _ ("Sorry, I could not put the job in background"));
3592
3593
3594 if (v == 1)
3595 {
3596 mc_setctl (panel->cwd_vpath, VFS_SETCTL_FORGET, NULL);
3597
3598 mc_setctl (dest_vpath, VFS_SETCTL_FORGET, NULL);
3599 vfs_path_free (dest_vpath, TRUE);
3600 g_free (dest);
3601
3602 return FALSE;
3603 }
3604 }
3605 else
3606 #endif
3607 {
3608 const file_entry_t *fe;
3609
3610 if (operation == OP_DELETE)
3611 dialog_type = FILEGUI_DIALOG_DELETE_ITEM;
3612 else if (single_entry
3613 && ((fe = panel_current_entry (panel)) == NULL ? FALSE : S_ISDIR (fe->st.st_mode)))
3614 dialog_type = FILEGUI_DIALOG_MULTI_ITEM;
3615 else if (single_entry || force_single)
3616 dialog_type = FILEGUI_DIALOG_ONE_ITEM;
3617 else
3618 dialog_type = FILEGUI_DIALOG_MULTI_ITEM;
3619 }
3620
3621
3622
3623
3624
3625 if ((dest != NULL)
3626 && (mc_setctl (dest_vpath, VFS_SETCTL_STALE_DATA, GUINT_TO_POINTER (1)) != 0))
3627 save_dest = vfs_path_from_str (dest);
3628
3629 if ((vfs_path_tokens_count (panel->cwd_vpath) != 0)
3630 && (mc_setctl (panel->cwd_vpath, VFS_SETCTL_STALE_DATA, GUINT_TO_POINTER (1)) != 0))
3631 save_cwd = vfs_path_clone (panel->cwd_vpath);
3632
3633
3634
3635
3636 if (single_entry)
3637 {
3638
3639
3640
3641 if ((operation != OP_COPY) && (get_current_type () == view_tree))
3642 {
3643 vfs_path_t *vpath;
3644 int chdir_retcode;
3645
3646 vpath = vfs_path_from_str (PATH_SEP_STR);
3647 chdir_retcode = mc_chdir (vpath);
3648 vfs_path_free (vpath, TRUE);
3649 if (chdir_retcode < 0)
3650 {
3651 ret_val = FALSE;
3652 goto clean_up;
3653 }
3654 }
3655
3656 value = operate_single_file (panel, ctx, source, &src_stat, dest, dialog_type);
3657 if ((value == FILE_CONT) && !force_single)
3658 unmark_files (panel);
3659 }
3660 else
3661 {
3662
3663
3664
3665 while (operation != OP_DELETE)
3666 {
3667 int dst_result;
3668 struct stat dst_stat;
3669
3670 dst_result = mc_stat (dest_vpath, &dst_stat);
3671
3672 if ((dst_result != 0) || S_ISDIR (dst_stat.st_mode))
3673 break;
3674
3675 if (ctx->ignore_all
3676 || file_error (ctx, TRUE, _ ("Destination\n%s\nmust be a directory"), dest)
3677 != FILE_RETRY)
3678 goto clean_up;
3679 }
3680
3681
3682
3683
3684
3685
3686 value =
3687 panel_operate_init_totals (panel, NULL, NULL, ctx, file_op_compute_totals, dialog_type);
3688 if (value == FILE_CONT)
3689
3690 for (i = 0; i < panel->dir.len; i++)
3691 {
3692 const char *source2;
3693
3694 if (panel->dir.list[i].f.marked == 0)
3695 continue;
3696
3697 source2 = panel->dir.list[i].fname->str;
3698 src_stat = panel->dir.list[i].st;
3699
3700 value = operate_one_file (panel, ctx, source2, &src_stat, dest);
3701 if (value == FILE_ABORT)
3702 break;
3703
3704 if (value == FILE_CONT)
3705 do_file_mark (panel, i, 0);
3706
3707 mc_refresh ();
3708 }
3709 }
3710
3711 clean_up:
3712
3713 if (save_cwd != NULL)
3714 {
3715 mc_setctl (save_cwd, VFS_SETCTL_STALE_DATA, NULL);
3716 vfs_path_free (save_cwd, TRUE);
3717 }
3718
3719 if (save_dest != NULL)
3720 {
3721 mc_setctl (save_dest, VFS_SETCTL_STALE_DATA, NULL);
3722 vfs_path_free (save_dest, TRUE);
3723 }
3724
3725 linklist = free_linklist (linklist);
3726 dest_dirs = free_linklist (dest_dirs);
3727 g_free (dest);
3728 vfs_path_free (dest_vpath, TRUE);
3729 MC_PTR_FREE (ctx->dest_mask);
3730
3731 #ifdef ENABLE_BACKGROUND
3732
3733 if (mc_global.we_are_background)
3734 {
3735
3736
3737 ctx->pid = getpid ();
3738 parent_call ((void *) end_bg_process, ctx, 0);
3739
3740 vfs_shut ();
3741 my_exit (EXIT_SUCCESS);
3742 }
3743 #endif
3744
3745 ret_fast:
3746 file_op_context_destroy (ctx);
3747
3748 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
3749 repaint_screen ();
3750
3751 return ret_val;
3752 }
3753
3754
3755
3756
3757
3758
3759
3760 FileProgressStatus
3761 file_error (file_op_context_t *ctx, gboolean allow_retry, const char *format, const char *file)
3762 {
3763 return files_error (ctx, allow_retry, format, file, "");
3764 }
3765
3766
3767
3768
3769
3770
3771
3772