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