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