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