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 static int
1515 check_dir_is_empty (const vfs_path_t *vpath)
1516 {
1517 DIR *dir;
1518 struct vfs_dirent *d;
1519 int i = 1;
1520
1521 dir = mc_opendir (vpath);
1522 if (dir == NULL)
1523 return -1;
1524
1525 for (d = mc_readdir (dir); d != NULL; d = mc_readdir (dir))
1526 if (!DIR_IS_DOT (d->d_name) && !DIR_IS_DOTDOT (d->d_name))
1527 {
1528 i = 0;
1529 break;
1530 }
1531
1532 mc_closedir (dir);
1533 return i;
1534 }
1535
1536
1537
1538 static FileProgressStatus
1539 erase_dir_iff_empty (file_op_context_t *ctx, const vfs_path_t *vpath)
1540 {
1541 file_progress_show_deleting (ctx, vpath, NULL);
1542 file_progress_show_count (ctx);
1543 if (file_progress_check_buttons (ctx) == FILE_ABORT)
1544 return FILE_ABORT;
1545
1546 mc_refresh ();
1547
1548 if (check_dir_is_empty (vpath) != 1)
1549 return FILE_CONT;
1550
1551
1552 return try_erase_dir (ctx, vpath);
1553 }
1554
1555
1556
1557 static void
1558 erase_dir_after_copy (file_op_context_t *ctx, const vfs_path_t *vpath, FileProgressStatus *status)
1559 {
1560 if (ctx->erase_at_end && erase_list != NULL)
1561 {
1562
1563 ctx->total_progress_count = ctx->prev_total_progress_count;
1564
1565 while (!g_queue_is_empty (erase_list) && *status != FILE_ABORT)
1566 {
1567 link_t *lp;
1568
1569 lp = (link_t *) g_queue_pop_head (erase_list);
1570
1571 if (S_ISDIR (lp->st_mode))
1572 *status = erase_dir_iff_empty (ctx, lp->src_vpath);
1573 else
1574 *status = erase_file (ctx, lp->src_vpath);
1575
1576 free_link (lp);
1577 }
1578
1579
1580 ctx->prev_total_progress_count = ctx->total_progress_count;
1581 }
1582
1583 erase_dir_iff_empty (ctx, vpath);
1584 }
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600 static FileProgressStatus
1601 do_move_dir_dir (const WPanel *panel, file_op_context_t *ctx, const char *s, const char *d)
1602 {
1603 struct stat src_stat, dst_stat;
1604 FileProgressStatus return_status = FILE_CONT;
1605 gboolean move_over = FALSE;
1606 gboolean dstat_ok;
1607 vfs_path_t *src_vpath, *dst_vpath;
1608
1609 src_vpath = vfs_path_from_str (s);
1610 dst_vpath = vfs_path_from_str (d);
1611
1612 file_progress_show_source (ctx, src_vpath);
1613 file_progress_show_target (ctx, dst_vpath);
1614
1615
1616 if (panel != NULL && file_progress_check_buttons (ctx) == FILE_ABORT)
1617 {
1618 return_status = FILE_ABORT;
1619 goto ret_fast;
1620 }
1621
1622 mc_refresh ();
1623
1624 mc_stat (src_vpath, &src_stat);
1625
1626 dstat_ok = (mc_stat (dst_vpath, &dst_stat) == 0);
1627
1628 if (dstat_ok && check_same_file (ctx, s, &src_stat, d, &dst_stat, &return_status))
1629 goto ret_fast;
1630
1631 if (!dstat_ok)
1632 ;
1633 else if (!ctx->dive_into_subdirs)
1634 move_over = TRUE;
1635 else
1636 {
1637 vfs_path_t *tmp;
1638
1639 tmp = dst_vpath;
1640 dst_vpath = vfs_path_append_new (dst_vpath, x_basename (s), (char *) NULL);
1641 vfs_path_free (tmp, TRUE);
1642 }
1643
1644 d = vfs_path_as_str (dst_vpath);
1645
1646
1647 retry_dst_stat:
1648 if (mc_stat (dst_vpath, &dst_stat) == 0)
1649 {
1650 if (move_over)
1651 {
1652 if (panel != NULL)
1653 {
1654
1655
1656 return_status =
1657 panel_operate_init_totals (panel, src_vpath, &src_stat, ctx, TRUE,
1658 FILEGUI_DIALOG_MULTI_ITEM);
1659 if (return_status != FILE_CONT)
1660 goto ret;
1661 }
1662
1663 return_status = copy_dir_dir (ctx, s, d, FALSE, TRUE, TRUE, NULL);
1664
1665 if (return_status != FILE_CONT)
1666 goto ret;
1667 goto oktoret;
1668 }
1669 else if (ctx->ignore_all)
1670 return_status = FILE_IGNORE_ALL;
1671 else
1672 {
1673 if (S_ISDIR (dst_stat.st_mode))
1674 return_status =
1675 file_error (ctx, TRUE, _("Cannot overwrite directory \"%s\"\n%s"), d);
1676 else
1677 return_status = file_error (ctx, TRUE, _("Cannot overwrite file \"%s\"\n%s"), d);
1678 if (return_status == FILE_IGNORE_ALL)
1679 ctx->ignore_all = TRUE;
1680 if (return_status == FILE_RETRY)
1681 goto retry_dst_stat;
1682 }
1683
1684 goto ret_fast;
1685 }
1686
1687 retry_rename:
1688 if (mc_rename (src_vpath, dst_vpath) == 0)
1689 {
1690 return_status = FILE_CONT;
1691 goto ret;
1692 }
1693
1694 if (errno != EXDEV)
1695 {
1696 if (!ctx->ignore_all)
1697 {
1698 return_status =
1699 files_error (ctx, _("Cannot move directory \"%s\" to \"%s\"\n%s"), s, d);
1700 if (return_status == FILE_IGNORE_ALL)
1701 ctx->ignore_all = TRUE;
1702 if (return_status == FILE_RETRY)
1703 goto retry_rename;
1704 }
1705 goto ret;
1706 }
1707
1708
1709 if (panel != NULL)
1710 {
1711
1712
1713 return_status =
1714 panel_operate_init_totals (panel, src_vpath, &src_stat, ctx, TRUE,
1715 FILEGUI_DIALOG_MULTI_ITEM);
1716 if (return_status != FILE_CONT)
1717 goto ret;
1718 }
1719
1720 return_status = copy_dir_dir (ctx, s, d, FALSE, FALSE, TRUE, NULL);
1721
1722 if (return_status != FILE_CONT)
1723 goto ret;
1724
1725 oktoret:
1726
1727
1728 if (panel == NULL)
1729 {
1730 file_progress_show_source (ctx, NULL);
1731 file_progress_show_target (ctx, NULL);
1732 if (verbose)
1733 file_progress_show (ctx, 0, 0, "", FALSE);
1734
1735 return_status = file_progress_check_buttons (ctx);
1736 if (return_status != FILE_CONT)
1737 goto ret;
1738 }
1739
1740 mc_refresh ();
1741
1742 erase_dir_after_copy (ctx, src_vpath, &return_status);
1743
1744 ret:
1745 erase_list = free_erase_list (erase_list);
1746 ret_fast:
1747 vfs_path_free (src_vpath, TRUE);
1748 vfs_path_free (dst_vpath, TRUE);
1749 return return_status;
1750 }
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761 static const char *
1762 panel_get_file (const WPanel *panel)
1763 {
1764 const file_entry_t *fe;
1765
1766 if (get_current_type () == view_tree)
1767 {
1768 WTree *tree;
1769 const vfs_path_t *selected_name;
1770
1771 tree = (WTree *) get_panel_widget (get_current_index ());
1772 selected_name = tree_selected_name (tree);
1773 return vfs_path_as_str (selected_name);
1774 }
1775
1776 if (panel->marked != 0)
1777 {
1778 int i;
1779
1780 for (i = 0; i < panel->dir.len; i++)
1781 if (panel->dir.list[i].f.marked != 0)
1782 return panel->dir.list[i].fname->str;
1783 }
1784
1785 fe = panel_current_entry (panel);
1786
1787 return (fe == NULL ? NULL : fe->fname->str);
1788 }
1789
1790
1791
1792 static const char *
1793 check_single_entry (const WPanel *panel, gboolean force_single, struct stat *src_stat)
1794 {
1795 const char *source;
1796 gboolean ok;
1797
1798 if (force_single)
1799 {
1800 const file_entry_t *fe;
1801
1802 fe = panel_current_entry (panel);
1803 source = fe == NULL ? NULL : fe->fname->str;
1804 }
1805 else
1806 source = panel_get_file (panel);
1807
1808 if (source == NULL)
1809 return NULL;
1810
1811 ok = !DIR_IS_DOTDOT (source);
1812
1813 if (!ok)
1814 message (D_ERROR, MSG_ERROR, _("Cannot operate on \"..\"!"));
1815 else
1816 {
1817 vfs_path_t *source_vpath;
1818
1819 source_vpath = vfs_path_from_str (source);
1820
1821
1822 ok = mc_lstat (source_vpath, src_stat) == 0;
1823 if (!ok)
1824 {
1825 message (D_ERROR, MSG_ERROR, _("Cannot stat \"%s\"\n%s"),
1826 path_trunc (source, 30), unix_error_string (errno));
1827
1828
1829 if (!panel->is_panelized)
1830 {
1831 panel_update_flags_t flags = UP_RELOAD;
1832
1833
1834 if (get_other_type () == view_listing && other_panel->is_panelized)
1835 flags |= UP_ONLY_CURRENT;
1836
1837 update_panels (flags, UP_KEEPSEL);
1838 }
1839 }
1840
1841 vfs_path_free (source_vpath, TRUE);
1842 }
1843
1844 return ok ? source : NULL;
1845 }
1846
1847
1848
1849
1850
1851
1852
1853 static char *
1854 panel_operate_generate_prompt (const WPanel *panel, FileOperation operation,
1855 const struct stat *src_stat)
1856 {
1857 char *sp;
1858 char *format_string;
1859 const char *cp;
1860
1861 static gboolean i18n_flag = FALSE;
1862 if (!i18n_flag)
1863 {
1864 size_t i;
1865
1866 for (i = G_N_ELEMENTS (op_names1); i-- != 0;)
1867 op_names1[i] = Q_ (op_names1[i]);
1868
1869 #ifdef ENABLE_NLS
1870 for (i = G_N_ELEMENTS (prompt_parts); i-- != 0;)
1871 prompt_parts[i] = _(prompt_parts[i]);
1872
1873 one_format = _(one_format);
1874 many_format = _(many_format);
1875 #endif
1876 i18n_flag = TRUE;
1877 }
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900 cp = (src_stat != NULL ? one_format : many_format);
1901
1902
1903 format_string = str_replace_all (cp, "%o", op_names1[(int) operation]);
1904
1905
1906 cp = operation == OP_DELETE ? "\n" : " ";
1907 sp = format_string;
1908 format_string = str_replace_all (sp, "%n", cp);
1909 g_free (sp);
1910
1911
1912 if (src_stat != NULL)
1913 cp = S_ISDIR (src_stat->st_mode) ? prompt_parts[2] : prompt_parts[0];
1914 else if (panel->marked == panel->dirs_marked)
1915 cp = prompt_parts[3];
1916 else
1917 cp = panel->dirs_marked != 0 ? prompt_parts[4] : prompt_parts[1];
1918
1919 sp = format_string;
1920 format_string = str_replace_all (sp, "%f", cp);
1921 g_free (sp);
1922
1923
1924 cp = operation == OP_DELETE ? "?" : prompt_parts[5];
1925 sp = format_string;
1926 format_string = str_replace_all (sp, "%m", cp);
1927 g_free (sp);
1928
1929 return format_string;
1930 }
1931
1932
1933
1934 static char *
1935 do_confirm_copy_move (const WPanel *panel, gboolean force_single, const char *source,
1936 struct stat *src_stat, file_op_context_t *ctx, gboolean *do_bg)
1937 {
1938 const char *tmp_dest_dir;
1939 char *dest_dir;
1940 char *format;
1941 char *ret;
1942
1943
1944 if (force_single)
1945 tmp_dest_dir = source;
1946 else if (get_other_type () == view_listing)
1947 tmp_dest_dir = vfs_path_as_str (other_panel->cwd_vpath);
1948 else
1949 tmp_dest_dir = vfs_path_as_str (panel->cwd_vpath);
1950
1951
1952
1953
1954
1955
1956 if (!force_single && tmp_dest_dir != NULL && tmp_dest_dir[0] != '\0'
1957 && !IS_PATH_SEP (tmp_dest_dir[strlen (tmp_dest_dir) - 1]))
1958 {
1959
1960 dest_dir = g_strconcat (tmp_dest_dir, PATH_SEP_STR, (char *) NULL);
1961 }
1962 else
1963 {
1964
1965 dest_dir = g_strdup (tmp_dest_dir);
1966 }
1967
1968 if (dest_dir == NULL)
1969 return NULL;
1970
1971 if (source == NULL)
1972 src_stat = NULL;
1973
1974
1975 format = panel_operate_generate_prompt (panel, ctx->operation, src_stat);
1976
1977 ret = file_mask_dialog (ctx, source != NULL, format,
1978 source != NULL ? source : (const void *) &panel->marked, dest_dir,
1979 do_bg);
1980
1981 g_free (format);
1982 g_free (dest_dir);
1983
1984 return ret;
1985 }
1986
1987
1988
1989 static gboolean
1990 do_confirm_erase (const WPanel *panel, const char *source, struct stat *src_stat)
1991 {
1992 int i;
1993 char *format;
1994 char fmd_buf[BUF_MEDIUM];
1995
1996 if (source == NULL)
1997 src_stat = NULL;
1998
1999
2000 format = panel_operate_generate_prompt (panel, OP_DELETE, src_stat);
2001
2002 if (source == NULL)
2003 g_snprintf (fmd_buf, sizeof (fmd_buf), format, panel->marked);
2004 else
2005 {
2006 const int fmd_xlen = 64;
2007
2008 i = fmd_xlen - str_term_width1 (format) - 4;
2009 g_snprintf (fmd_buf, sizeof (fmd_buf), format, str_trunc (source, i));
2010 }
2011
2012 g_free (format);
2013
2014 if (safe_delete)
2015 query_set_sel (1);
2016
2017 i = query_dialog (op_names[OP_DELETE], fmd_buf, D_ERROR, 2, _("&Yes"), _("&No"));
2018
2019 return (i == 0);
2020 }
2021
2022
2023
2024 static FileProgressStatus
2025 operate_single_file (const WPanel *panel, file_op_context_t *ctx, const char *src,
2026 struct stat *src_stat, const char *dest, filegui_dialog_type_t dialog_type)
2027 {
2028 FileProgressStatus value;
2029 vfs_path_t *src_vpath;
2030 gboolean is_file;
2031
2032 if (g_path_is_absolute (src))
2033 src_vpath = vfs_path_from_str (src);
2034 else
2035 src_vpath = vfs_path_append_new (panel->cwd_vpath, src, (char *) NULL);
2036
2037 is_file = !S_ISDIR (src_stat->st_mode);
2038
2039 if (is_file)
2040 {
2041 gboolean is_link;
2042
2043 is_link = file_is_symlink_to_dir (src_vpath, src_stat, NULL);
2044 is_file = !(is_link && ctx->follow_links);
2045 }
2046
2047 if (ctx->operation == OP_DELETE)
2048 {
2049 value = panel_operate_init_totals (panel, src_vpath, src_stat, ctx, !is_file, dialog_type);
2050 if (value == FILE_CONT)
2051 {
2052 if (is_file)
2053 value = erase_file (ctx, src_vpath);
2054 else
2055 value = erase_dir (ctx, src_vpath);
2056 }
2057 }
2058 else
2059 {
2060 char *temp;
2061
2062 src = vfs_path_as_str (src_vpath);
2063
2064 temp = build_dest (ctx, src, dest, &value);
2065 if (temp != NULL)
2066 {
2067 dest = temp;
2068
2069 switch (ctx->operation)
2070 {
2071 case OP_COPY:
2072
2073 ctx->stat_func (src_vpath, src_stat);
2074
2075 value =
2076 panel_operate_init_totals (panel, src_vpath, src_stat, ctx, !is_file,
2077 dialog_type);
2078 if (value == FILE_CONT)
2079 {
2080 is_file = !S_ISDIR (src_stat->st_mode);
2081
2082 if (is_file)
2083 {
2084 gboolean is_link;
2085
2086 is_link = file_is_symlink_to_dir (src_vpath, src_stat, NULL);
2087 is_file = !(is_link && ctx->follow_links);
2088 }
2089
2090 if (is_file)
2091 value = copy_file_file (ctx, src, dest);
2092 else
2093 value = copy_dir_dir (ctx, src, dest, TRUE, FALSE, FALSE, NULL);
2094 }
2095 break;
2096
2097 case OP_MOVE:
2098 #ifdef ENABLE_BACKGROUND
2099 if (!mc_global.we_are_background)
2100 #endif
2101
2102 file_progress_ui_create (ctx, TRUE, FILEGUI_DIALOG_ONE_ITEM);
2103
2104 if (is_file)
2105 value = move_file_file (panel, ctx, src, dest);
2106 else
2107 value = do_move_dir_dir (panel, ctx, src, dest);
2108 break;
2109
2110 default:
2111
2112 abort ();
2113 }
2114
2115 g_free (temp);
2116 }
2117 }
2118
2119 vfs_path_free (src_vpath, TRUE);
2120
2121 return value;
2122 }
2123
2124
2125
2126 static FileProgressStatus
2127 operate_one_file (const WPanel *panel, file_op_context_t *ctx, const char *src,
2128 struct stat *src_stat, const char *dest)
2129 {
2130 FileProgressStatus value = FILE_CONT;
2131 vfs_path_t *src_vpath;
2132 gboolean is_file;
2133
2134 if (g_path_is_absolute (src))
2135 src_vpath = vfs_path_from_str (src);
2136 else
2137 src_vpath = vfs_path_append_new (panel->cwd_vpath, src, (char *) NULL);
2138
2139 is_file = !S_ISDIR (src_stat->st_mode);
2140
2141 if (ctx->operation == OP_DELETE)
2142 {
2143 if (is_file)
2144 value = erase_file (ctx, src_vpath);
2145 else
2146 value = erase_dir (ctx, src_vpath);
2147 }
2148 else
2149 {
2150 char *temp;
2151
2152 src = vfs_path_as_str (src_vpath);
2153
2154 temp = build_dest (ctx, src, dest, &value);
2155 if (temp != NULL)
2156 {
2157 dest = temp;
2158
2159 switch (ctx->operation)
2160 {
2161 case OP_COPY:
2162
2163 ctx->stat_func (src_vpath, src_stat);
2164 is_file = !S_ISDIR (src_stat->st_mode);
2165
2166 if (is_file)
2167 value = copy_file_file (ctx, src, dest);
2168 else
2169 value = copy_dir_dir (ctx, src, dest, TRUE, FALSE, FALSE, NULL);
2170 dest_dirs = free_linklist (dest_dirs);
2171 break;
2172
2173 case OP_MOVE:
2174 if (is_file)
2175 value = move_file_file (NULL, ctx, src, dest);
2176 else
2177 value = do_move_dir_dir (NULL, ctx, src, dest);
2178 break;
2179
2180 default:
2181
2182 abort ();
2183 }
2184
2185 g_free (temp);
2186 }
2187 }
2188
2189 vfs_path_free (src_vpath, TRUE);
2190
2191 return value;
2192 }
2193
2194
2195
2196 #ifdef ENABLE_BACKGROUND
2197 static int
2198 end_bg_process (file_op_context_t *ctx, enum OperationMode mode)
2199 {
2200 int pid = ctx->pid;
2201
2202 (void) mode;
2203 ctx->pid = 0;
2204
2205 unregister_task_with_pid (pid);
2206
2207 return 1;
2208 }
2209 #endif
2210
2211
2212
2213
2214
2215
2216
2217
2218 static inline gboolean
2219 attrs_ignore_error (const int e)
2220 {
2221 return (e == ENOTSUP || e == EOPNOTSUPP || e == ENOSYS || e == EINVAL || e == ENOTTY
2222 || e == ELOOP || e == ENXIO);
2223 }
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237 gboolean
2238 file_is_symlink_to_dir (const vfs_path_t *vpath, struct stat *st, gboolean *stale_link)
2239 {
2240 struct stat st2;
2241 gboolean stale = FALSE;
2242 gboolean res = FALSE;
2243
2244 if (st == NULL)
2245 {
2246 st = &st2;
2247
2248 if (mc_lstat (vpath, st) != 0)
2249 goto ret;
2250 }
2251
2252 if (S_ISLNK (st->st_mode))
2253 {
2254 struct stat st3;
2255
2256 stale = (mc_stat (vpath, &st3) != 0);
2257
2258 if (!stale)
2259 res = (S_ISDIR (st3.st_mode) != 0);
2260 }
2261
2262 ret:
2263 if (stale_link != NULL)
2264 *stale_link = stale;
2265
2266 return res;
2267 }
2268
2269
2270
2271 FileProgressStatus
2272 copy_file_file (file_op_context_t *ctx, const char *src_path, const char *dst_path)
2273 {
2274 uid_t src_uid = (uid_t) (-1);
2275 gid_t src_gid = (gid_t) (-1);
2276
2277 int src_desc, dest_desc = -1;
2278 mode_t src_mode = 0;
2279 struct stat src_stat, dst_stat;
2280 mc_timesbuf_t times;
2281 unsigned long attrs = 0;
2282 gboolean attrs_ok = ctx->preserve;
2283 gboolean dst_exists = FALSE, appending = FALSE;
2284 off_t file_size = -1;
2285 FileProgressStatus return_status, temp_status;
2286 dest_status_t dst_status = DEST_NONE;
2287 int open_flags;
2288 vfs_path_t *src_vpath = NULL, *dst_vpath = NULL;
2289 char *buf = NULL;
2290
2291
2292
2293
2294 if (ctx->do_reget < 0)
2295 ctx->do_reget = 0;
2296
2297 return_status = FILE_RETRY;
2298
2299 dst_vpath = vfs_path_from_str (dst_path);
2300 src_vpath = vfs_path_from_str (src_path);
2301
2302 file_progress_show_source (ctx, src_vpath);
2303 file_progress_show_target (ctx, dst_vpath);
2304
2305 if (file_progress_check_buttons (ctx) == FILE_ABORT)
2306 {
2307 return_status = FILE_ABORT;
2308 goto ret_fast;
2309 }
2310
2311 mc_refresh ();
2312
2313 while (mc_stat (dst_vpath, &dst_stat) == 0)
2314 {
2315 if (S_ISDIR (dst_stat.st_mode))
2316 {
2317 if (ctx->ignore_all)
2318 return_status = FILE_IGNORE_ALL;
2319 else
2320 {
2321 return_status =
2322 file_error (ctx, TRUE, _("Cannot overwrite directory \"%s\"\n%s"), dst_path);
2323 if (return_status == FILE_IGNORE_ALL)
2324 ctx->ignore_all = TRUE;
2325 if (return_status == FILE_RETRY)
2326 continue;
2327 }
2328 goto ret_fast;
2329 }
2330
2331 dst_exists = TRUE;
2332 break;
2333 }
2334
2335 while ((*ctx->stat_func) (src_vpath, &src_stat) != 0)
2336 {
2337 if (ctx->ignore_all)
2338 return_status = FILE_IGNORE_ALL;
2339 else
2340 {
2341 return_status =
2342 file_error (ctx, TRUE, _("Cannot stat source file \"%s\"\n%s"), src_path);
2343 if (return_status == FILE_IGNORE_ALL)
2344 ctx->ignore_all = TRUE;
2345 }
2346
2347 if (return_status != FILE_RETRY)
2348 {
2349
2350 progress_update_one (FALSE, ctx, 0);
2351 goto ret_fast;
2352 }
2353 }
2354
2355
2356 src_mode = src_stat.st_mode;
2357 src_uid = src_stat.st_uid;
2358 src_gid = src_stat.st_gid;
2359 file_size = src_stat.st_size;
2360
2361 while (attrs_ok && mc_fgetflags (src_vpath, &attrs) != 0)
2362 {
2363 attrs_ok = FALSE;
2364
2365
2366 if (attrs_ignore_error (errno))
2367 return_status = FILE_CONT;
2368 else if (ctx->ignore_all)
2369 return_status = FILE_IGNORE_ALL;
2370 else
2371 {
2372 return_status =
2373 file_error (ctx, TRUE, _("Cannot get ext2 attributes of source file \"%s\"\n%s"),
2374 src_path);
2375 if (return_status == FILE_IGNORE_ALL)
2376 ctx->ignore_all = TRUE;
2377 if (return_status == FILE_ABORT)
2378 goto ret_fast;
2379 }
2380
2381 if (return_status != FILE_RETRY)
2382 break;
2383
2384
2385 attrs_ok = TRUE;
2386 }
2387
2388 if (dst_exists)
2389 {
2390
2391 if (check_same_file (ctx, src_path, &src_stat, dst_path, &dst_stat, &return_status))
2392 goto ret_fast;
2393
2394
2395 if (ctx->ask_overwrite)
2396 {
2397 ctx->do_reget = 0;
2398 return_status = query_replace (ctx, src_path, &src_stat, dst_path, &dst_stat);
2399 if (return_status != FILE_CONT)
2400 goto ret_fast;
2401 }
2402 }
2403
2404 vfs_get_timesbuf_from_stat (&src_stat, ×);
2405
2406 if (!ctx->do_append)
2407 {
2408
2409 if (!ctx->follow_links)
2410 {
2411 switch (check_hardlinks (ctx, src_vpath, &src_stat, dst_vpath, &ctx->ignore_all))
2412 {
2413 case HARDLINK_OK:
2414
2415 return_status = FILE_CONT;
2416 goto ret_fast;
2417
2418 case HARDLINK_ABORT:
2419 return_status = FILE_ABORT;
2420 goto ret_fast;
2421
2422 default:
2423 break;
2424 }
2425 }
2426
2427 if (S_ISLNK (src_stat.st_mode))
2428 {
2429 return_status = make_symlink (ctx, src_vpath, dst_vpath);
2430 if (return_status == FILE_CONT && ctx->preserve)
2431 {
2432 mc_utime (dst_vpath, ×);
2433
2434 while (attrs_ok && mc_fsetflags (dst_vpath, attrs) != 0 && !ctx->ignore_all)
2435 {
2436 attrs_ok = FALSE;
2437
2438
2439 if (attrs_ignore_error (errno))
2440 return_status = FILE_CONT;
2441 else if (return_status == FILE_IGNORE_ALL)
2442 ctx->ignore_all = TRUE;
2443 else
2444 return_status =
2445 file_error (ctx, TRUE,
2446 _("Cannot set ext2 attributes of target file \"%s\"\n%s"),
2447 dst_path);
2448
2449 if (return_status != FILE_RETRY)
2450 break;
2451
2452
2453 attrs_ok = TRUE;
2454 }
2455 }
2456 goto ret_fast;
2457 }
2458
2459 if (S_ISCHR (src_stat.st_mode) || S_ISBLK (src_stat.st_mode) || S_ISFIFO (src_stat.st_mode)
2460 || S_ISNAM (src_stat.st_mode) || S_ISSOCK (src_stat.st_mode))
2461 {
2462 dev_t rdev = 0;
2463
2464 #ifdef HAVE_STRUCT_STAT_ST_RDEV
2465 rdev = src_stat.st_rdev;
2466 #endif
2467
2468 while (mc_mknod (dst_vpath, src_stat.st_mode & ctx->umask_kill, rdev) < 0
2469 && !ctx->ignore_all)
2470 {
2471 return_status =
2472 file_error (ctx, TRUE, _("Cannot create special file \"%s\"\n%s"), dst_path);
2473 if (return_status == FILE_RETRY)
2474 continue;
2475 if (return_status == FILE_IGNORE_ALL)
2476 ctx->ignore_all = TRUE;
2477 goto ret_fast;
2478 }
2479
2480
2481 while (ctx->preserve_uidgid
2482 && mc_chown (dst_vpath, src_stat.st_uid, src_stat.st_gid) != 0
2483 && !ctx->ignore_all)
2484 {
2485 temp_status =
2486 file_error (ctx, TRUE, _("Cannot chown target file \"%s\"\n%s"), dst_path);
2487 if (temp_status == FILE_IGNORE)
2488 break;
2489 if (temp_status == FILE_IGNORE_ALL)
2490 ctx->ignore_all = TRUE;
2491 if (temp_status != FILE_RETRY)
2492 {
2493 return_status = temp_status;
2494 goto ret_fast;
2495 }
2496 }
2497
2498 while (ctx->preserve && mc_chmod (dst_vpath, src_stat.st_mode & ctx->umask_kill) != 0
2499 && !ctx->ignore_all)
2500 {
2501 temp_status =
2502 file_error (ctx, TRUE, _("Cannot chmod target file \"%s\"\n%s"), dst_path);
2503 if (temp_status == FILE_IGNORE)
2504 break;
2505 if (temp_status == FILE_IGNORE_ALL)
2506 ctx->ignore_all = TRUE;
2507 if (temp_status != FILE_RETRY)
2508 {
2509 return_status = temp_status;
2510 goto ret_fast;
2511 }
2512 }
2513
2514 while (attrs_ok && mc_fsetflags (dst_vpath, attrs) != 0 && !ctx->ignore_all)
2515 {
2516 attrs_ok = FALSE;
2517
2518
2519 if (attrs_ignore_error (errno))
2520 break;
2521
2522 temp_status =
2523 file_error (ctx, TRUE, _("Cannot set ext2 attributes of target file \"%s\"\n%s"),
2524 dst_path);
2525 if (temp_status == FILE_IGNORE)
2526 break;
2527 if (temp_status == FILE_IGNORE_ALL)
2528 ctx->ignore_all = TRUE;
2529 if (temp_status != FILE_RETRY)
2530 {
2531 return_status = temp_status;
2532 goto ret_fast;
2533 }
2534
2535
2536 attrs_ok = TRUE;
2537 }
2538
2539 return_status = FILE_CONT;
2540 mc_utime (dst_vpath, ×);
2541 goto ret_fast;
2542 }
2543 }
2544
2545 ctx->transfer_start = g_get_monotonic_time ();
2546
2547 while ((src_desc = mc_open (src_vpath, O_RDONLY | O_LINEAR)) < 0)
2548 {
2549 if (ctx->ignore_all)
2550 return_status = FILE_IGNORE_ALL;
2551 else
2552 {
2553 return_status =
2554 file_error (ctx, TRUE, _("Cannot open source file \"%s\"\n%s"), src_path);
2555 if (return_status == FILE_RETRY)
2556 continue;
2557 if (return_status == FILE_IGNORE_ALL)
2558 ctx->ignore_all = TRUE;
2559 ctx->do_append = FALSE;
2560 }
2561 goto ret;
2562 }
2563
2564 if (ctx->do_reget != 0 && mc_lseek (src_desc, ctx->do_reget, SEEK_SET) != ctx->do_reget)
2565 {
2566 message (D_ERROR, _("Warning"), _("Reget failed, about to overwrite file"));
2567 ctx->do_reget = 0;
2568 ctx->do_append = FALSE;
2569 }
2570
2571 while (mc_fstat (src_desc, &src_stat) != 0)
2572 {
2573 if (ctx->ignore_all)
2574 return_status = FILE_IGNORE_ALL;
2575 else
2576 {
2577 return_status =
2578 file_error (ctx, TRUE, _("Cannot fstat source file \"%s\"\n%s"), src_path);
2579 if (return_status == FILE_RETRY)
2580 continue;
2581 if (return_status == FILE_IGNORE_ALL)
2582 ctx->ignore_all = TRUE;
2583 ctx->do_append = FALSE;
2584 }
2585 goto ret;
2586 }
2587
2588
2589 src_mode = src_stat.st_mode;
2590 src_uid = src_stat.st_uid;
2591 src_gid = src_stat.st_gid;
2592 file_size = src_stat.st_size;
2593
2594 open_flags = O_WRONLY;
2595 if (!dst_exists)
2596 open_flags |= O_CREAT | O_EXCL;
2597 else if (ctx->do_append)
2598 open_flags |= O_APPEND;
2599 else
2600 open_flags |= O_CREAT | O_TRUNC;
2601
2602 while ((dest_desc = mc_open (dst_vpath, open_flags, src_mode)) < 0)
2603 {
2604 if (errno != EEXIST)
2605 {
2606 if (ctx->ignore_all)
2607 return_status = FILE_IGNORE_ALL;
2608 else
2609 {
2610 return_status =
2611 file_error (ctx, TRUE, _("Cannot create target file \"%s\"\n%s"), dst_path);
2612 if (return_status == FILE_RETRY)
2613 continue;
2614 if (return_status == FILE_IGNORE_ALL)
2615 ctx->ignore_all = TRUE;
2616 ctx->do_append = FALSE;
2617 }
2618 }
2619 goto ret;
2620 }
2621
2622
2623 dst_status = DEST_SHORT_QUERY;
2624
2625 appending = ctx->do_append;
2626 ctx->do_append = FALSE;
2627
2628
2629 if (vfs_clone_file (dest_desc, src_desc) == 0)
2630 {
2631 dst_status = DEST_FULL;
2632 return_status = FILE_CONT;
2633 goto ret;
2634 }
2635
2636
2637 while (mc_fstat (dest_desc, &dst_stat) != 0)
2638 {
2639 if (ctx->ignore_all)
2640 return_status = FILE_IGNORE_ALL;
2641 else
2642 {
2643 return_status =
2644 file_error (ctx, TRUE, _("Cannot fstat target file \"%s\"\n%s"), dst_path);
2645 if (return_status == FILE_RETRY)
2646 continue;
2647 if (return_status == FILE_IGNORE_ALL)
2648 ctx->ignore_all = TRUE;
2649 }
2650 goto ret;
2651 }
2652
2653
2654 while (mc_global.vfs.preallocate_space &&
2655 vfs_preallocate (dest_desc, file_size, appending ? dst_stat.st_size : 0) != 0)
2656 {
2657 if (ctx->ignore_all)
2658 {
2659
2660 return_status = FILE_CONT;
2661 break;
2662 }
2663
2664 return_status =
2665 file_error (ctx, TRUE, _("Cannot preallocate space for target file \"%s\"\n%s"),
2666 dst_path);
2667
2668 if (return_status == FILE_IGNORE_ALL)
2669 ctx->ignore_all = TRUE;
2670
2671 if (ctx->ignore_all || return_status == FILE_IGNORE)
2672 {
2673
2674 return_status = FILE_CONT;
2675 break;
2676 }
2677
2678 if (return_status == FILE_ABORT)
2679 {
2680 mc_close (dest_desc);
2681 dest_desc = -1;
2682 mc_unlink (dst_vpath);
2683 dst_status = DEST_NONE;
2684 goto ret;
2685 }
2686
2687
2688 }
2689
2690 ctx->eta_secs = 0.0;
2691 ctx->bps = 0;
2692
2693 if (verbose)
2694 {
2695 if (ctx->total_bps == 0 || (file_size / ctx->total_bps) > FILEOP_UPDATE_INTERVAL)
2696 file_progress_show (ctx, 0, file_size, "", TRUE);
2697 else
2698 file_progress_show (ctx, 1, 1, "", TRUE);
2699 }
2700
2701 return_status = file_progress_check_buttons (ctx);
2702 mc_refresh ();
2703
2704 if (return_status == FILE_CONT)
2705 {
2706 off_t file_part = 0;
2707 gint64 tv_last_update = ctx->transfer_start;
2708 gint64 tv_last_input = 0;
2709 gboolean is_first_time = TRUE;
2710
2711 const size_t bufsize = io_blksize (dst_stat);
2712 buf = g_malloc (bufsize);
2713
2714 while (TRUE)
2715 {
2716 ssize_t n_read = -1;
2717
2718
2719 if (mc_ctl (src_desc, VFS_CTL_IS_NOTREADY, 0) == 0)
2720 while ((n_read = mc_read (src_desc, buf, bufsize)) < 0 && !ctx->ignore_all)
2721 {
2722 return_status =
2723 file_error (ctx, TRUE, _("Cannot read source file \"%s\"\n%s"), src_path);
2724 if (return_status == FILE_RETRY)
2725 continue;
2726 if (return_status == FILE_IGNORE_ALL)
2727 ctx->ignore_all = TRUE;
2728 goto ret;
2729 }
2730
2731 if (n_read == 0)
2732 break;
2733
2734 const gint64 tv_current = g_get_monotonic_time ();
2735
2736 if (n_read > 0)
2737 {
2738 ssize_t n_written;
2739 char *t = buf;
2740
2741 file_part += n_read;
2742
2743 tv_last_input = tv_current;
2744
2745
2746 while ((n_written = mc_write (dest_desc, t, (size_t) n_read)) < n_read)
2747 {
2748 gboolean write_errno_nospace;
2749
2750 if (n_written > 0)
2751 {
2752 n_read -= n_written;
2753 t += n_written;
2754 continue;
2755 }
2756
2757 write_errno_nospace = (n_written < 0 && errno == ENOSPC);
2758
2759 if (ctx->ignore_all)
2760 return_status = FILE_IGNORE_ALL;
2761 else
2762 return_status =
2763 file_error (ctx, TRUE, _("Cannot write target file \"%s\"\n%s"),
2764 dst_path);
2765
2766 if (return_status == FILE_IGNORE)
2767 {
2768 if (write_errno_nospace)
2769 goto ret;
2770 break;
2771 }
2772 if (return_status == FILE_IGNORE_ALL)
2773 {
2774 ctx->ignore_all = TRUE;
2775 if (write_errno_nospace)
2776 goto ret;
2777 }
2778 if (return_status != FILE_RETRY)
2779 goto ret;
2780 }
2781 }
2782
2783 ctx->progress_bytes = file_part + ctx->do_reget;
2784
2785 const gint64 usecs = tv_current - tv_last_update;
2786
2787 if (is_first_time || usecs > FILEOP_UPDATE_INTERVAL_US)
2788 {
2789 calc_copy_file_progress (ctx, tv_current, file_part, file_size - ctx->do_reget);
2790 tv_last_update = tv_current;
2791 }
2792
2793 is_first_time = FALSE;
2794
2795 if (verbose)
2796 {
2797 const gint64 total_usecs = tv_current - ctx->total_transfer_start;
2798 const gboolean force_update = total_usecs > FILEOP_UPDATE_INTERVAL_US;
2799
2800 const gint64 update_usecs = tv_current - tv_last_input;
2801 const char *stalled_msg =
2802 update_usecs > FILEOP_STALLING_INTERVAL_US ? _("(stalled)") : "";
2803
2804 file_progress_show (ctx, ctx->progress_bytes, file_size, stalled_msg, force_update);
2805 if (ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
2806 {
2807 file_progress_show_count (ctx);
2808 file_progress_show_total (ctx, ctx->total_progress_bytes + ctx->progress_bytes,
2809 tv_current, force_update);
2810 }
2811
2812 mc_refresh ();
2813 }
2814
2815 return_status = file_progress_check_buttons (ctx);
2816 if (return_status != FILE_CONT)
2817 {
2818 int query_res;
2819
2820 const gint64 t1 = g_get_monotonic_time ();
2821 query_res =
2822 query_dialog (Q_ ("DialogTitle|Copy"),
2823 _("Incomplete file was retrieved"), D_ERROR, 3,
2824 _("&Delete"), _("&Keep"), _("&Continue copy"));
2825 const gint64 t2 = g_get_monotonic_time ();
2826 ctx->pauses += t2 - t1;
2827
2828
2829 calc_copy_file_progress (ctx, t2, file_part, file_size - ctx->do_reget);
2830
2831 switch (query_res)
2832 {
2833 case 0:
2834
2835 dst_status = DEST_SHORT_DELETE;
2836 goto ret;
2837
2838 case 1:
2839
2840 dst_status = DEST_SHORT_KEEP;
2841 goto ret;
2842
2843 default:
2844
2845 break;
2846 }
2847 }
2848 }
2849
2850
2851 dst_status = DEST_FULL;
2852 }
2853
2854 ret:
2855 g_free (buf);
2856
2857 rotate_dash (FALSE);
2858 while (src_desc != -1 && mc_close (src_desc) < 0 && !ctx->ignore_all)
2859 {
2860 temp_status = file_error (ctx, TRUE, _("Cannot close source file \"%s\"\n%s"), src_path);
2861 if (temp_status == FILE_RETRY)
2862 continue;
2863 if (temp_status == FILE_ABORT)
2864 return_status = temp_status;
2865 if (temp_status == FILE_IGNORE_ALL)
2866 ctx->ignore_all = TRUE;
2867 break;
2868 }
2869
2870 while (dest_desc != -1 && mc_close (dest_desc) < 0 && !ctx->ignore_all)
2871 {
2872 temp_status = file_error (ctx, TRUE, _("Cannot close target file \"%s\"\n%s"), dst_path);
2873 if (temp_status == FILE_RETRY)
2874 continue;
2875 if (temp_status == FILE_IGNORE_ALL)
2876 ctx->ignore_all = TRUE;
2877 return_status = temp_status;
2878 break;
2879 }
2880
2881 if (dst_status == DEST_SHORT_QUERY)
2882 {
2883
2884 if (query_dialog (Q_ ("DialogTitle|Copy"), _("Incomplete file was retrieved"),
2885 D_ERROR, 2, _("&Delete"), _("&Keep")) == 0)
2886 dst_status = DEST_SHORT_DELETE;
2887 else
2888 dst_status = DEST_SHORT_KEEP;
2889 }
2890
2891 if (dst_status == DEST_SHORT_DELETE)
2892 mc_unlink (dst_vpath);
2893 else if (dst_status == DEST_FULL && !appending)
2894 {
2895
2896
2897 while (ctx->preserve_uidgid && mc_chown (dst_vpath, src_uid, src_gid) != 0
2898 && !ctx->ignore_all)
2899 {
2900 temp_status = file_error (ctx, TRUE, _("Cannot chown target file \"%s\"\n%s"), dst_path);
2901 if (temp_status == FILE_ABORT)
2902 {
2903 return_status = FILE_ABORT;
2904 goto ret_fast;
2905 }
2906 if (temp_status == FILE_RETRY)
2907 continue;
2908 if (temp_status == FILE_IGNORE_ALL)
2909 {
2910 ctx->ignore_all = TRUE;
2911 return_status = FILE_CONT;
2912 }
2913 if (temp_status == FILE_IGNORE)
2914 return_status = FILE_CONT;
2915 break;
2916 }
2917
2918 while (ctx->preserve && mc_chmod (dst_vpath, (src_mode & ctx->umask_kill)) != 0
2919 && !ctx->ignore_all)
2920 {
2921 temp_status = file_error (ctx, TRUE, _("Cannot chmod target file \"%s\"\n%s"), dst_path);
2922 if (temp_status == FILE_ABORT)
2923 {
2924 return_status = FILE_ABORT;
2925 goto ret_fast;
2926 }
2927 if (temp_status == FILE_RETRY)
2928 continue;
2929 if (temp_status == FILE_IGNORE_ALL)
2930 {
2931 ctx->ignore_all = TRUE;
2932 return_status = FILE_CONT;
2933 }
2934 if (temp_status == FILE_IGNORE)
2935 return_status = FILE_CONT;
2936 break;
2937 }
2938
2939 if (!ctx->preserve && !dst_exists)
2940 {
2941 src_mode = umask (-1);
2942 umask (src_mode);
2943 src_mode = 0100666 & ~src_mode;
2944 mc_chmod (dst_vpath, (src_mode & ctx->umask_kill));
2945 }
2946 }
2947
2948 if (dst_status == DEST_FULL || dst_status == DEST_SHORT_KEEP)
2949 {
2950
2951 mc_utime (dst_vpath, ×);
2952
2953 while (attrs_ok && mc_fsetflags (dst_vpath, attrs) != 0 && !ctx->ignore_all)
2954 {
2955 attrs_ok = FALSE;
2956
2957
2958 if (attrs_ignore_error (errno))
2959 {
2960 return_status = FILE_CONT;
2961 break;
2962 }
2963
2964 temp_status =
2965 file_error (ctx, TRUE, _("Cannot set ext2 attributes for target file \"%s\"\n%s"),
2966 dst_path);
2967 if (temp_status == FILE_ABORT)
2968 return_status = FILE_ABORT;
2969 if (temp_status == FILE_RETRY)
2970 {
2971 attrs_ok = TRUE;
2972 continue;
2973 }
2974 if (temp_status == FILE_IGNORE_ALL)
2975 {
2976 ctx->ignore_all = TRUE;
2977 return_status = FILE_CONT;
2978 }
2979 if (temp_status == FILE_IGNORE)
2980 return_status = FILE_CONT;
2981 break;
2982 }
2983 }
2984
2985 progress_update_one (return_status == FILE_CONT, ctx, file_size);
2986 if (return_status == FILE_CONT)
2987 return_status = file_progress_check_buttons (ctx);
2988
2989 ret_fast:
2990 vfs_path_free (src_vpath, TRUE);
2991 vfs_path_free (dst_vpath, TRUE);
2992 return return_status;
2993 }
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003 FileProgressStatus
3004 copy_dir_dir (file_op_context_t *ctx, const char *s, const char *d, gboolean toplevel,
3005 gboolean move_over, gboolean do_delete, GSList *parent_dirs)
3006 {
3007 struct vfs_dirent *next;
3008 struct stat dst_stat, src_stat;
3009 unsigned long attrs = 0;
3010 gboolean attrs_ok = ctx->preserve;
3011 DIR *reading;
3012 FileProgressStatus return_status = FILE_CONT;
3013 link_t *lp;
3014 vfs_path_t *src_vpath, *dst_vpath;
3015 gboolean do_mkdir = TRUE;
3016
3017 src_vpath = vfs_path_from_str (s);
3018 dst_vpath = vfs_path_from_str (d);
3019
3020
3021
3022 retry_src_stat:
3023 while ((*ctx->stat_func) (src_vpath, &src_stat) != 0)
3024 {
3025 if (ctx->ignore_all)
3026 return_status = FILE_IGNORE_ALL;
3027 else
3028 {
3029 return_status = file_error (ctx, TRUE, _("Cannot stat source directory \"%s\"\n%s"), s);
3030 if (return_status == FILE_IGNORE_ALL)
3031 ctx->ignore_all = TRUE;
3032 }
3033
3034 if (return_status != FILE_RETRY)
3035 goto ret_fast;
3036 }
3037
3038 while (attrs_ok && mc_fgetflags (src_vpath, &attrs) != 0)
3039 {
3040 attrs_ok = FALSE;
3041
3042
3043 if (attrs_ignore_error (errno))
3044 return_status = FILE_CONT;
3045 else if (ctx->ignore_all)
3046 return_status = FILE_IGNORE_ALL;
3047 else
3048 {
3049 return_status =
3050 file_error (ctx, TRUE,
3051 _("Cannot get ext2 attributes of source directory \"%s\"\n%s"), s);
3052 if (return_status == FILE_IGNORE_ALL)
3053 ctx->ignore_all = TRUE;
3054 if (return_status == FILE_ABORT)
3055 goto ret_fast;
3056 }
3057
3058 if (return_status != FILE_RETRY)
3059 break;
3060
3061
3062 attrs_ok = TRUE;
3063 }
3064
3065 if (is_in_linklist (dest_dirs, src_vpath, &src_stat) != NULL)
3066 {
3067
3068
3069
3070 return_status = FILE_CONT;
3071 goto ret_fast;
3072 }
3073
3074
3075
3076
3077 if (ctx->preserve)
3078 {
3079 switch (check_hardlinks (ctx, src_vpath, &src_stat, dst_vpath, &ctx->ignore_all))
3080 {
3081 case HARDLINK_OK:
3082
3083 goto ret_fast;
3084
3085 case HARDLINK_ABORT:
3086 return_status = FILE_ABORT;
3087 goto ret_fast;
3088
3089 default:
3090 break;
3091 }
3092 }
3093
3094 if (!S_ISDIR (src_stat.st_mode))
3095 {
3096 if (ctx->ignore_all)
3097 return_status = FILE_IGNORE_ALL;
3098 else
3099 {
3100 return_status = file_error (ctx, TRUE, _("Source \"%s\" is not a directory\n%s"), s);
3101 if (return_status == FILE_RETRY)
3102 goto retry_src_stat;
3103 if (return_status == FILE_IGNORE_ALL)
3104 ctx->ignore_all = TRUE;
3105 }
3106 goto ret_fast;
3107 }
3108
3109 if (is_in_linklist (parent_dirs, src_vpath, &src_stat) != NULL)
3110 {
3111
3112 message (D_ERROR, MSG_ERROR, _("Cannot copy cyclic symbolic link\n\"%s\""), s);
3113 return_status = FILE_SKIP;
3114 goto ret_fast;
3115 }
3116
3117 lp = g_new0 (link_t, 1);
3118 lp->vfs = vfs_path_get_last_path_vfs (src_vpath);
3119 lp->ino = src_stat.st_ino;
3120 lp->dev = src_stat.st_dev;
3121 parent_dirs = g_slist_prepend (parent_dirs, lp);
3122
3123 retry_dst_stat:
3124
3125 if (mc_stat (dst_vpath, &dst_stat) != 0)
3126 {
3127
3128 if (move_over && mc_rename (src_vpath, dst_vpath) == 0)
3129 {
3130 return_status = FILE_CONT;
3131 goto ret;
3132 }
3133 }
3134 else
3135 {
3136
3137
3138
3139
3140
3141
3142
3143
3144 if (!S_ISDIR (dst_stat.st_mode))
3145 {
3146 if (ctx->ignore_all)
3147 return_status = FILE_IGNORE_ALL;
3148 else
3149 {
3150 return_status =
3151 file_error (ctx, TRUE, _("Destination \"%s\" must be a directory\n%s"), d);
3152 if (return_status == FILE_IGNORE_ALL)
3153 ctx->ignore_all = TRUE;
3154 if (return_status == FILE_RETRY)
3155 goto retry_dst_stat;
3156 }
3157 goto ret;
3158 }
3159
3160 if (toplevel && ctx->dive_into_subdirs)
3161 {
3162 vfs_path_t *tmp;
3163
3164 tmp = dst_vpath;
3165 dst_vpath = vfs_path_append_new (dst_vpath, x_basename (s), (char *) NULL);
3166 vfs_path_free (tmp, TRUE);
3167
3168 }
3169 else
3170 do_mkdir = FALSE;
3171 }
3172
3173 d = vfs_path_as_str (dst_vpath);
3174
3175 if (do_mkdir)
3176 {
3177 while (my_mkdir (dst_vpath, (src_stat.st_mode & ctx->umask_kill) | S_IRWXU) != 0)
3178 {
3179 if (ctx->ignore_all)
3180 return_status = FILE_IGNORE_ALL;
3181 else
3182 {
3183 return_status =
3184 file_error (ctx, TRUE, _("Cannot create target directory \"%s\"\n%s"), d);
3185 if (return_status == FILE_IGNORE_ALL)
3186 ctx->ignore_all = TRUE;
3187 }
3188 if (return_status != FILE_RETRY)
3189 goto ret;
3190 }
3191
3192 lp = g_new0 (link_t, 1);
3193 mc_stat (dst_vpath, &dst_stat);
3194 lp->vfs = vfs_path_get_last_path_vfs (dst_vpath);
3195 lp->ino = dst_stat.st_ino;
3196 lp->dev = dst_stat.st_dev;
3197 dest_dirs = g_slist_prepend (dest_dirs, lp);
3198 }
3199
3200 if (ctx->preserve_uidgid)
3201 {
3202 while (mc_chown (dst_vpath, src_stat.st_uid, src_stat.st_gid) != 0)
3203 {
3204 if (ctx->ignore_all)
3205 return_status = FILE_IGNORE_ALL;
3206 else
3207 {
3208 return_status =
3209 file_error (ctx, TRUE, _("Cannot chown target directory \"%s\"\n%s"), d);
3210 if (return_status == FILE_IGNORE_ALL)
3211 ctx->ignore_all = TRUE;
3212 }
3213 if (return_status != FILE_RETRY)
3214 goto ret;
3215 }
3216 }
3217
3218
3219 reading = mc_opendir (src_vpath);
3220 if (reading == NULL)
3221 goto ret;
3222
3223 while ((next = mc_readdir (reading)) && return_status != FILE_ABORT)
3224 {
3225 char *path;
3226 vfs_path_t *tmp_vpath;
3227
3228
3229
3230
3231 if (DIR_IS_DOT (next->d_name) || DIR_IS_DOTDOT (next->d_name))
3232 continue;
3233
3234
3235 path = mc_build_filename (s, next->d_name, (char *) NULL);
3236 tmp_vpath = vfs_path_from_str (path);
3237
3238 (*ctx->stat_func) (tmp_vpath, &dst_stat);
3239 if (S_ISDIR (dst_stat.st_mode))
3240 {
3241 char *mdpath;
3242
3243 mdpath = mc_build_filename (d, next->d_name, (char *) NULL);
3244
3245
3246
3247
3248
3249
3250 return_status = copy_dir_dir (ctx, path, mdpath, FALSE, FALSE, do_delete, parent_dirs);
3251 g_free (mdpath);
3252 }
3253 else
3254 {
3255 char *dest_file;
3256
3257 dest_file = mc_build_filename (d, x_basename (path), (char *) NULL);
3258 return_status = copy_file_file (ctx, path, dest_file);
3259 g_free (dest_file);
3260 }
3261
3262 g_free (path);
3263
3264 if (do_delete && return_status == FILE_CONT)
3265 {
3266 if (ctx->erase_at_end)
3267 {
3268 if (erase_list == NULL)
3269 erase_list = g_queue_new ();
3270
3271 lp = g_new0 (link_t, 1);
3272 lp->src_vpath = tmp_vpath;
3273 lp->st_mode = dst_stat.st_mode;
3274 g_queue_push_tail (erase_list, lp);
3275 tmp_vpath = NULL;
3276 }
3277 else if (S_ISDIR (dst_stat.st_mode))
3278 return_status = erase_dir_iff_empty (ctx, tmp_vpath);
3279 else
3280 return_status = erase_file (ctx, tmp_vpath);
3281 }
3282 vfs_path_free (tmp_vpath, TRUE);
3283 }
3284 mc_closedir (reading);
3285
3286 if (ctx->preserve)
3287 {
3288 mc_timesbuf_t times;
3289
3290 mc_chmod (dst_vpath, src_stat.st_mode & ctx->umask_kill);
3291
3292 if (attrs_ok)
3293 mc_fsetflags (dst_vpath, attrs);
3294
3295 vfs_get_timesbuf_from_stat (&src_stat, ×);
3296 mc_utime (dst_vpath, ×);
3297 }
3298 else
3299 {
3300 src_stat.st_mode = umask (-1);
3301 umask (src_stat.st_mode);
3302 src_stat.st_mode = 0100777 & ~src_stat.st_mode;
3303 mc_chmod (dst_vpath, src_stat.st_mode & ctx->umask_kill);
3304 }
3305
3306 ret:
3307 free_link (parent_dirs->data);
3308 g_slist_free_1 (parent_dirs);
3309 ret_fast:
3310 vfs_path_free (src_vpath, TRUE);
3311 vfs_path_free (dst_vpath, TRUE);
3312 return return_status;
3313 }
3314
3315
3316
3317
3318
3319
3320 FileProgressStatus
3321 move_dir_dir (file_op_context_t *ctx, const char *s, const char *d)
3322 {
3323 return do_move_dir_dir (NULL, ctx, s, d);
3324 }
3325
3326
3327
3328
3329
3330
3331 FileProgressStatus
3332 erase_dir (file_op_context_t *ctx, const vfs_path_t *vpath)
3333 {
3334 file_progress_show_deleting (ctx, vpath, NULL);
3335 file_progress_show_count (ctx);
3336 if (file_progress_check_buttons (ctx) == FILE_ABORT)
3337 return FILE_ABORT;
3338
3339 mc_refresh ();
3340
3341
3342
3343
3344
3345
3346
3347
3348 if (check_dir_is_empty (vpath) == 0)
3349 {
3350 FileProgressStatus error;
3351
3352 error = query_recursive (ctx, vfs_path_as_str (vpath));
3353 if (error == FILE_CONT)
3354 error = recursive_erase (ctx, vpath);
3355 return error;
3356 }
3357
3358 return try_erase_dir (ctx, vpath);
3359 }
3360
3361
3362
3363
3364
3365
3366 void
3367 dirsize_status_init_cb (status_msg_t *sm)
3368 {
3369 dirsize_status_msg_t *dsm = (dirsize_status_msg_t *) sm;
3370 WGroup *gd = GROUP (sm->dlg);
3371 Widget *wd = WIDGET (sm->dlg);
3372 WRect r = wd->rect;
3373
3374 const char *b1_name = N_("&Abort");
3375 const char *b2_name = N_("&Skip");
3376 int b_width, ui_width;
3377
3378 #ifdef ENABLE_NLS
3379 b1_name = _(b1_name);
3380 b2_name = _(b2_name);
3381 #endif
3382
3383 b_width = str_term_width1 (b1_name) + 4;
3384 if (dsm->allow_skip)
3385 b_width += str_term_width1 (b2_name) + 4 + 1;
3386
3387 ui_width = MAX (COLS / 2, b_width + 6);
3388 dsm->dirname = label_new (2, 3, NULL);
3389 group_add_widget (gd, dsm->dirname);
3390 dsm->count_size = label_new (3, 3, NULL);
3391 group_add_widget (gd, dsm->count_size);
3392 group_add_widget (gd, hline_new (4, -1, -1));
3393
3394 dsm->abort_button = WIDGET (button_new (5, 3, FILE_ABORT, NORMAL_BUTTON, b1_name, NULL));
3395 group_add_widget (gd, dsm->abort_button);
3396 if (dsm->allow_skip)
3397 {
3398 dsm->skip_button = WIDGET (button_new (5, 3, FILE_SKIP, NORMAL_BUTTON, b2_name, NULL));
3399 group_add_widget (gd, dsm->skip_button);
3400 widget_select (dsm->skip_button);
3401 }
3402
3403 r.lines = 8;
3404 r.cols = ui_width;
3405 widget_set_size_rect (wd, &r);
3406 dirsize_status_locate_buttons (dsm);
3407 }
3408
3409
3410
3411 int
3412 dirsize_status_update_cb (status_msg_t *sm)
3413 {
3414 dirsize_status_msg_t *dsm = (dirsize_status_msg_t *) sm;
3415 Widget *wd = WIDGET (sm->dlg);
3416 WRect r = wd->rect;
3417
3418
3419 label_set_textv (dsm->count_size, _("Directories: %zu, total size: %s"),
3420 dsm->dir_count, size_trunc_sep (dsm->total_size, panels_options.kilobyte_si));
3421
3422
3423 if (WIDGET (dsm->count_size)->rect.cols + 6 > r.cols)
3424 {
3425 r.cols = WIDGET (dsm->count_size)->rect.cols + 6;
3426 widget_set_size_rect (wd, &r);
3427 dirsize_status_locate_buttons (dsm);
3428 widget_draw (wd);
3429
3430 }
3431
3432
3433 label_set_text (dsm->dirname,
3434 str_trunc (vfs_path_as_str (dsm->dirname_vpath), wd->rect.cols - 6));
3435
3436 switch (status_msg_common_update (sm))
3437 {
3438 case B_CANCEL:
3439 case FILE_ABORT:
3440 return FILE_ABORT;
3441 case FILE_SKIP:
3442 return FILE_SKIP;
3443 default:
3444 return FILE_CONT;
3445 }
3446 }
3447
3448
3449
3450 void
3451 dirsize_status_deinit_cb (status_msg_t *sm)
3452 {
3453 (void) sm;
3454
3455
3456 if (get_other_type () == view_listing)
3457 other_panel->dirty = TRUE;
3458 }
3459
3460
3461
3462
3463
3464
3465
3466
3467 FileProgressStatus
3468 compute_dir_size (const vfs_path_t *dirname_vpath, dirsize_status_msg_t *sm,
3469 size_t *ret_dir_count, size_t *ret_marked_count, uintmax_t *ret_total,
3470 gboolean follow_symlinks)
3471 {
3472 return do_compute_dir_size (dirname_vpath, sm, ret_dir_count, ret_marked_count, ret_total,
3473 follow_symlinks ? mc_stat : mc_lstat);
3474 }
3475
3476
3477
3478
3479
3480
3481
3482
3483
3484
3485
3486
3487
3488
3489
3490 gboolean
3491 panel_operate (void *source_panel, FileOperation operation, gboolean force_single)
3492 {
3493 WPanel *panel = PANEL (source_panel);
3494 const gboolean single_entry = force_single || (panel->marked <= 1)
3495 || (get_current_type () == view_tree);
3496
3497 const char *source = NULL;
3498 char *dest = NULL;
3499 vfs_path_t *dest_vpath = NULL;
3500 vfs_path_t *save_cwd = NULL, *save_dest = NULL;
3501 struct stat src_stat;
3502 gboolean ret_val = TRUE;
3503 int i;
3504 FileProgressStatus value;
3505 file_op_context_t *ctx;
3506 filegui_dialog_type_t dialog_type = FILEGUI_DIALOG_ONE_ITEM;
3507
3508 gboolean do_bg = FALSE;
3509
3510 static gboolean i18n_flag = FALSE;
3511 if (!i18n_flag)
3512 {
3513 for (i = G_N_ELEMENTS (op_names); i-- != 0;)
3514 op_names[i] = Q_ (op_names[i]);
3515 i18n_flag = TRUE;
3516 }
3517
3518 linklist = free_linklist (linklist);
3519 dest_dirs = free_linklist (dest_dirs);
3520
3521 save_cwds_stat ();
3522
3523 if (single_entry)
3524 {
3525 source = check_single_entry (panel, force_single, &src_stat);
3526
3527 if (source == NULL)
3528 return FALSE;
3529 }
3530
3531 ctx = file_op_context_new (operation);
3532
3533
3534 if (operation != OP_DELETE)
3535 {
3536 dest = do_confirm_copy_move (panel, force_single, source, &src_stat, ctx, &do_bg);
3537 if (dest == NULL)
3538 {
3539 ret_val = FALSE;
3540 goto ret_fast;
3541 }
3542
3543 dest_vpath = vfs_path_from_str (dest);
3544 }
3545 else if (confirm_delete && !do_confirm_erase (panel, source, &src_stat))
3546 {
3547 ret_val = FALSE;
3548 goto ret_fast;
3549 }
3550
3551 ctx->total_transfer_start = g_get_monotonic_time ();
3552
3553 #ifdef ENABLE_BACKGROUND
3554
3555 if (do_bg)
3556 {
3557 int v;
3558
3559 v = do_background (ctx,
3560 g_strconcat (op_names[operation], ": ",
3561 vfs_path_as_str (panel->cwd_vpath), (char *) NULL));
3562 if (v == -1)
3563 message (D_ERROR, MSG_ERROR, _("Sorry, I could not put the job in background"));
3564
3565
3566 if (v == 1)
3567 {
3568 mc_setctl (panel->cwd_vpath, VFS_SETCTL_FORGET, NULL);
3569
3570 mc_setctl (dest_vpath, VFS_SETCTL_FORGET, NULL);
3571 vfs_path_free (dest_vpath, TRUE);
3572 g_free (dest);
3573
3574 return FALSE;
3575 }
3576 }
3577 else
3578 #endif
3579 {
3580 const file_entry_t *fe;
3581
3582 if (operation == OP_DELETE)
3583 dialog_type = FILEGUI_DIALOG_DELETE_ITEM;
3584 else if (single_entry
3585 && ((fe = panel_current_entry (panel)) == NULL ? FALSE : S_ISDIR (fe->st.st_mode)))
3586 dialog_type = FILEGUI_DIALOG_MULTI_ITEM;
3587 else if (single_entry || force_single)
3588 dialog_type = FILEGUI_DIALOG_ONE_ITEM;
3589 else
3590 dialog_type = FILEGUI_DIALOG_MULTI_ITEM;
3591 }
3592
3593
3594
3595
3596
3597 if ((dest != NULL)
3598 && (mc_setctl (dest_vpath, VFS_SETCTL_STALE_DATA, GUINT_TO_POINTER (1)) != 0))
3599 save_dest = vfs_path_from_str (dest);
3600
3601 if ((vfs_path_tokens_count (panel->cwd_vpath) != 0)
3602 && (mc_setctl (panel->cwd_vpath, VFS_SETCTL_STALE_DATA, GUINT_TO_POINTER (1)) != 0))
3603 save_cwd = vfs_path_clone (panel->cwd_vpath);
3604
3605
3606
3607
3608 if (single_entry)
3609 {
3610
3611
3612
3613 if ((operation != OP_COPY) && (get_current_type () == view_tree))
3614 {
3615 vfs_path_t *vpath;
3616 int chdir_retcode;
3617
3618 vpath = vfs_path_from_str (PATH_SEP_STR);
3619 chdir_retcode = mc_chdir (vpath);
3620 vfs_path_free (vpath, TRUE);
3621 if (chdir_retcode < 0)
3622 {
3623 ret_val = FALSE;
3624 goto clean_up;
3625 }
3626 }
3627
3628 value = operate_single_file (panel, ctx, source, &src_stat, dest, dialog_type);
3629 if ((value == FILE_CONT) && !force_single)
3630 unmark_files (panel);
3631 }
3632 else
3633 {
3634
3635
3636
3637 while (operation != OP_DELETE)
3638 {
3639 int dst_result;
3640 struct stat dst_stat;
3641
3642 dst_result = mc_stat (dest_vpath, &dst_stat);
3643
3644 if ((dst_result != 0) || S_ISDIR (dst_stat.st_mode))
3645 break;
3646
3647 if (ctx->ignore_all
3648 || file_error (ctx, TRUE, _("Destination \"%s\" must be a directory\n%s"),
3649 dest) != FILE_RETRY)
3650 goto clean_up;
3651 }
3652
3653
3654
3655
3656
3657
3658 value =
3659 panel_operate_init_totals (panel, NULL, NULL, ctx, file_op_compute_totals, dialog_type);
3660 if (value == FILE_CONT)
3661
3662 for (i = 0; i < panel->dir.len; i++)
3663 {
3664 const char *source2;
3665
3666 if (panel->dir.list[i].f.marked == 0)
3667 continue;
3668
3669 source2 = panel->dir.list[i].fname->str;
3670 src_stat = panel->dir.list[i].st;
3671
3672 value = operate_one_file (panel, ctx, source2, &src_stat, dest);
3673 if (value == FILE_ABORT)
3674 break;
3675
3676 if (value == FILE_CONT)
3677 do_file_mark (panel, i, 0);
3678
3679 mc_refresh ();
3680 }
3681 }
3682
3683 clean_up:
3684
3685 if (save_cwd != NULL)
3686 {
3687 mc_setctl (save_cwd, VFS_SETCTL_STALE_DATA, NULL);
3688 vfs_path_free (save_cwd, TRUE);
3689 }
3690
3691 if (save_dest != NULL)
3692 {
3693 mc_setctl (save_dest, VFS_SETCTL_STALE_DATA, NULL);
3694 vfs_path_free (save_dest, TRUE);
3695 }
3696
3697 linklist = free_linklist (linklist);
3698 dest_dirs = free_linklist (dest_dirs);
3699 g_free (dest);
3700 vfs_path_free (dest_vpath, TRUE);
3701 MC_PTR_FREE (ctx->dest_mask);
3702
3703 #ifdef ENABLE_BACKGROUND
3704
3705 if (mc_global.we_are_background)
3706 {
3707
3708
3709 ctx->pid = getpid ();
3710 parent_call ((void *) end_bg_process, ctx, 0);
3711
3712 vfs_shut ();
3713 my_exit (EXIT_SUCCESS);
3714 }
3715 #endif
3716
3717 ret_fast:
3718 file_op_context_destroy (ctx);
3719
3720 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
3721 repaint_screen ();
3722
3723 return ret_val;
3724 }
3725
3726
3727
3728
3729
3730
3731 FileProgressStatus
3732 file_error (file_op_context_t *ctx, gboolean allow_retry, const char *format, const char *file)
3733 {
3734 char buf[BUF_MEDIUM];
3735
3736 g_snprintf (buf, sizeof (buf), format, path_trunc (file, 30), unix_error_string (errno));
3737
3738 return do_file_error (ctx, allow_retry, buf);
3739 }
3740
3741
3742
3743
3744
3745
3746
3747