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 unsigned long attrs;
2276 gboolean attrs_ok = ctx->preserve;
2277 gboolean dst_exists = FALSE, appending = FALSE;
2278 off_t file_size = -1;
2279 FileProgressStatus return_status, temp_status;
2280 gint64 tv_transfer_start;
2281 dest_status_t dst_status = DEST_NONE;
2282 int open_flags;
2283 vfs_path_t *src_vpath = NULL, *dst_vpath = NULL;
2284 char *buf = NULL;
2285
2286
2287 ctx->do_reget = 0;
2288 return_status = FILE_RETRY;
2289
2290 dst_vpath = vfs_path_from_str (dst_path);
2291 src_vpath = vfs_path_from_str (src_path);
2292
2293 file_progress_show_source (ctx, src_vpath);
2294 file_progress_show_target (ctx, dst_vpath);
2295
2296 if (check_progress_buttons (ctx) == FILE_ABORT)
2297 {
2298 return_status = FILE_ABORT;
2299 goto ret_fast;
2300 }
2301
2302 mc_refresh ();
2303
2304 while (mc_stat (dst_vpath, &dst_stat) == 0)
2305 {
2306 if (S_ISDIR (dst_stat.st_mode))
2307 {
2308 if (ctx->skip_all)
2309 return_status = FILE_SKIPALL;
2310 else
2311 {
2312 return_status =
2313 file_error (TRUE, _("Cannot overwrite directory \"%s\"\n%s"), dst_path);
2314 if (return_status == FILE_SKIPALL)
2315 ctx->skip_all = TRUE;
2316 if (return_status == FILE_RETRY)
2317 continue;
2318 }
2319 goto ret_fast;
2320 }
2321
2322 dst_exists = TRUE;
2323 break;
2324 }
2325
2326 while ((*ctx->stat_func) (src_vpath, &src_stat) != 0)
2327 {
2328 if (ctx->skip_all)
2329 return_status = FILE_SKIPALL;
2330 else
2331 {
2332 return_status = file_error (TRUE, _("Cannot stat source file \"%s\"\n%s"), src_path);
2333 if (return_status == FILE_SKIPALL)
2334 ctx->skip_all = TRUE;
2335 }
2336
2337 if (return_status != FILE_RETRY)
2338 goto ret_fast;
2339 }
2340
2341 while (attrs_ok && mc_fgetflags (src_vpath, &attrs) != 0)
2342 {
2343 attrs_ok = FALSE;
2344
2345
2346 if (errno == ENOTSUP)
2347 return_status = FILE_CONT;
2348 else if (ctx->skip_all)
2349 return_status = FILE_SKIPALL;
2350 else
2351 {
2352 return_status =
2353 file_error (TRUE, _("Cannot get attributes of source file \"%s\"\n%s"), src_path);
2354 if (return_status == FILE_SKIPALL)
2355 ctx->skip_all = TRUE;
2356 }
2357
2358 if (return_status != FILE_RETRY)
2359 break;
2360
2361
2362 attrs_ok = TRUE;
2363 }
2364
2365 if (dst_exists)
2366 {
2367
2368 if (check_same_file (src_path, &src_stat, dst_path, &dst_stat, &return_status))
2369 goto ret_fast;
2370
2371
2372 if (tctx->ask_overwrite)
2373 {
2374 ctx->do_reget = 0;
2375 return_status = query_replace (ctx, src_path, &src_stat, dst_path, &dst_stat);
2376 if (return_status != FILE_CONT)
2377 goto ret_fast;
2378 }
2379 }
2380
2381 get_times (&src_stat, ×);
2382
2383 if (!ctx->do_append)
2384 {
2385
2386 if (!ctx->follow_links)
2387 {
2388 switch (check_hardlinks (src_vpath, &src_stat, dst_vpath, &ctx->skip_all))
2389 {
2390 case HARDLINK_OK:
2391
2392 return_status = FILE_CONT;
2393 goto ret_fast;
2394
2395 case HARDLINK_ABORT:
2396 return_status = FILE_ABORT;
2397 goto ret_fast;
2398
2399 default:
2400 break;
2401 }
2402 }
2403
2404 if (S_ISLNK (src_stat.st_mode))
2405 {
2406 return_status = make_symlink (ctx, src_vpath, dst_vpath);
2407 if (return_status == FILE_CONT && ctx->preserve)
2408 {
2409 mc_utime (dst_vpath, ×);
2410
2411 while (attrs_ok && mc_fsetflags (dst_vpath, attrs) != 0 && !ctx->skip_all)
2412 {
2413 attrs_ok = FALSE;
2414
2415
2416 if (errno == ENOTSUP)
2417 return_status = FILE_CONT;
2418 else if (return_status == FILE_SKIPALL)
2419 ctx->skip_all = TRUE;
2420 else
2421 return_status =
2422 file_error (TRUE, _("Cannot set attributes of target file \"%s\"\n%s"),
2423 dst_path);
2424
2425 if (return_status != FILE_RETRY)
2426 break;
2427
2428
2429 attrs_ok = TRUE;
2430 }
2431 }
2432 goto ret_fast;
2433 }
2434
2435 if (S_ISCHR (src_stat.st_mode) || S_ISBLK (src_stat.st_mode) || S_ISFIFO (src_stat.st_mode)
2436 || S_ISNAM (src_stat.st_mode) || S_ISSOCK (src_stat.st_mode))
2437 {
2438 dev_t rdev = 0;
2439
2440 #ifdef HAVE_STRUCT_STAT_ST_RDEV
2441 rdev = src_stat.st_rdev;
2442 #endif
2443
2444 while (mc_mknod (dst_vpath, src_stat.st_mode & ctx->umask_kill, rdev) < 0
2445 && !ctx->skip_all)
2446 {
2447 return_status =
2448 file_error (TRUE, _("Cannot create special file \"%s\"\n%s"), dst_path);
2449 if (return_status == FILE_RETRY)
2450 continue;
2451 if (return_status == FILE_SKIPALL)
2452 ctx->skip_all = TRUE;
2453 goto ret_fast;
2454 }
2455
2456
2457 while (ctx->preserve_uidgid
2458 && mc_chown (dst_vpath, src_stat.st_uid, src_stat.st_gid) != 0 && !ctx->skip_all)
2459 {
2460 temp_status = file_error (TRUE, _("Cannot chown target file \"%s\"\n%s"), dst_path);
2461 if (temp_status == FILE_SKIP)
2462 break;
2463 if (temp_status == FILE_SKIPALL)
2464 ctx->skip_all = TRUE;
2465 if (temp_status != FILE_RETRY)
2466 {
2467 return_status = temp_status;
2468 goto ret_fast;
2469 }
2470 }
2471
2472 while (ctx->preserve && mc_chmod (dst_vpath, src_stat.st_mode & ctx->umask_kill) != 0
2473 && !ctx->skip_all)
2474 {
2475 temp_status = file_error (TRUE, _("Cannot chmod target file \"%s\"\n%s"), dst_path);
2476 if (temp_status == FILE_SKIP)
2477 break;
2478 if (temp_status == FILE_SKIPALL)
2479 ctx->skip_all = TRUE;
2480 if (temp_status != FILE_RETRY)
2481 {
2482 return_status = temp_status;
2483 goto ret_fast;
2484 }
2485 }
2486
2487 while (attrs_ok && mc_fsetflags (dst_vpath, attrs) != 0 && !ctx->skip_all)
2488 {
2489 attrs_ok = FALSE;
2490
2491
2492 if (errno == ENOTSUP)
2493 break;
2494
2495 temp_status =
2496 file_error (TRUE, _("Cannot set attributes of target file \"%s\"\n%s"),
2497 dst_path);
2498 if (temp_status == FILE_SKIP)
2499 break;
2500 if (temp_status == FILE_SKIPALL)
2501 ctx->skip_all = TRUE;
2502 if (temp_status != FILE_RETRY)
2503 {
2504 return_status = temp_status;
2505 goto ret_fast;
2506 }
2507
2508
2509 attrs_ok = TRUE;
2510 }
2511
2512 return_status = FILE_CONT;
2513 mc_utime (dst_vpath, ×);
2514 goto ret_fast;
2515 }
2516 }
2517
2518 tv_transfer_start = g_get_monotonic_time ();
2519
2520 while ((src_desc = mc_open (src_vpath, O_RDONLY | O_LINEAR)) < 0 && !ctx->skip_all)
2521 {
2522 return_status = file_error (TRUE, _("Cannot open source file \"%s\"\n%s"), src_path);
2523 if (return_status == FILE_RETRY)
2524 continue;
2525 if (return_status == FILE_SKIPALL)
2526 ctx->skip_all = TRUE;
2527 if (return_status == FILE_SKIP)
2528 break;
2529 ctx->do_append = FALSE;
2530 goto ret_fast;
2531 }
2532
2533 if (ctx->do_reget != 0 && mc_lseek (src_desc, ctx->do_reget, SEEK_SET) != ctx->do_reget)
2534 {
2535 message (D_ERROR, _("Warning"), _("Reget failed, about to overwrite file"));
2536 ctx->do_reget = 0;
2537 ctx->do_append = FALSE;
2538 }
2539
2540 while (mc_fstat (src_desc, &src_stat) != 0)
2541 {
2542 if (ctx->skip_all)
2543 return_status = FILE_SKIPALL;
2544 else
2545 {
2546 return_status = file_error (TRUE, _("Cannot fstat source file \"%s\"\n%s"), src_path);
2547 if (return_status == FILE_RETRY)
2548 continue;
2549 if (return_status == FILE_SKIPALL)
2550 ctx->skip_all = TRUE;
2551 ctx->do_append = FALSE;
2552 }
2553 goto ret;
2554 }
2555
2556 src_mode = src_stat.st_mode;
2557 src_uid = src_stat.st_uid;
2558 src_gid = src_stat.st_gid;
2559 file_size = src_stat.st_size;
2560
2561 open_flags = O_WRONLY;
2562 if (!dst_exists)
2563 open_flags |= O_CREAT | O_EXCL;
2564 else if (ctx->do_append)
2565 open_flags |= O_APPEND;
2566 else
2567 open_flags |= O_CREAT | O_TRUNC;
2568
2569 while ((dest_desc = mc_open (dst_vpath, open_flags, src_mode)) < 0)
2570 {
2571 if (errno != EEXIST)
2572 {
2573 if (ctx->skip_all)
2574 return_status = FILE_SKIPALL;
2575 else
2576 {
2577 return_status =
2578 file_error (TRUE, _("Cannot create target file \"%s\"\n%s"), dst_path);
2579 if (return_status == FILE_RETRY)
2580 continue;
2581 if (return_status == FILE_SKIPALL)
2582 ctx->skip_all = TRUE;
2583 ctx->do_append = FALSE;
2584 }
2585 }
2586 goto ret;
2587 }
2588
2589
2590 dst_status = DEST_SHORT_QUERY;
2591
2592 appending = ctx->do_append;
2593 ctx->do_append = FALSE;
2594
2595
2596 if (vfs_clone_file (dest_desc, src_desc) == 0)
2597 {
2598 dst_status = DEST_FULL;
2599 return_status = FILE_CONT;
2600 goto ret;
2601 }
2602
2603
2604 while (mc_fstat (dest_desc, &dst_stat) != 0)
2605 {
2606 if (ctx->skip_all)
2607 return_status = FILE_SKIPALL;
2608 else
2609 {
2610 return_status = file_error (TRUE, _("Cannot fstat target file \"%s\"\n%s"), dst_path);
2611 if (return_status == FILE_RETRY)
2612 continue;
2613 if (return_status == FILE_SKIPALL)
2614 ctx->skip_all = TRUE;
2615 }
2616 goto ret;
2617 }
2618
2619
2620 while (mc_global.vfs.preallocate_space &&
2621 vfs_preallocate (dest_desc, file_size, appending ? dst_stat.st_size : 0) != 0)
2622 {
2623 if (ctx->skip_all)
2624 {
2625
2626 return_status = FILE_CONT;
2627 break;
2628 }
2629
2630 return_status =
2631 file_error (TRUE, _("Cannot preallocate space for target file \"%s\"\n%s"), dst_path);
2632
2633 if (return_status == FILE_SKIPALL)
2634 ctx->skip_all = TRUE;
2635
2636 if (ctx->skip_all || return_status == FILE_SKIP)
2637 {
2638
2639 return_status = FILE_CONT;
2640 break;
2641 }
2642
2643 if (return_status == FILE_ABORT)
2644 {
2645 mc_close (dest_desc);
2646 dest_desc = -1;
2647 mc_unlink (dst_vpath);
2648 dst_status = DEST_NONE;
2649 goto ret;
2650 }
2651
2652
2653 }
2654
2655 ctx->eta_secs = 0.0;
2656 ctx->bps = 0;
2657
2658 if (tctx->bps == 0 || (file_size / tctx->bps) > FILEOP_UPDATE_INTERVAL)
2659 file_progress_show (ctx, 0, file_size, "", TRUE);
2660 else
2661 file_progress_show (ctx, 1, 1, "", TRUE);
2662 return_status = check_progress_buttons (ctx);
2663 mc_refresh ();
2664
2665 if (return_status == FILE_CONT)
2666 {
2667 size_t bufsize;
2668 off_t file_part = 0;
2669 gint64 tv_current, tv_last_update;
2670 gint64 tv_last_input = 0;
2671 gint64 usecs, update_usecs;
2672 const char *stalled_msg = "";
2673 gboolean is_first_time = TRUE;
2674
2675 tv_last_update = tv_transfer_start;
2676
2677 bufsize = io_blksize (dst_stat);
2678 buf = g_malloc (bufsize);
2679
2680 while (TRUE)
2681 {
2682 ssize_t n_read = -1, n_written;
2683 gboolean force_update;
2684
2685
2686 if (mc_ctl (src_desc, VFS_CTL_IS_NOTREADY, 0) == 0)
2687 while ((n_read = mc_read (src_desc, buf, bufsize)) < 0 && !ctx->skip_all)
2688 {
2689 return_status =
2690 file_error (TRUE, _("Cannot read source file \"%s\"\n%s"), src_path);
2691 if (return_status == FILE_RETRY)
2692 continue;
2693 if (return_status == FILE_SKIPALL)
2694 ctx->skip_all = TRUE;
2695 goto ret;
2696 }
2697
2698 if (n_read == 0)
2699 break;
2700
2701 tv_current = g_get_monotonic_time ();
2702
2703 if (n_read > 0)
2704 {
2705 char *t = buf;
2706
2707 file_part += n_read;
2708
2709 tv_last_input = tv_current;
2710
2711
2712 while ((n_written = mc_write (dest_desc, t, (size_t) n_read)) < n_read)
2713 {
2714 gboolean write_errno_nospace;
2715
2716 if (n_written > 0)
2717 {
2718 n_read -= n_written;
2719 t += n_written;
2720 continue;
2721 }
2722
2723 write_errno_nospace = (n_written < 0 && errno == ENOSPC);
2724
2725 if (ctx->skip_all)
2726 return_status = FILE_SKIPALL;
2727 else
2728 return_status =
2729 file_error (TRUE, _("Cannot write target file \"%s\"\n%s"), dst_path);
2730
2731 if (return_status == FILE_SKIP)
2732 {
2733 if (write_errno_nospace)
2734 goto ret;
2735 break;
2736 }
2737 if (return_status == FILE_SKIPALL)
2738 {
2739 ctx->skip_all = TRUE;
2740 if (write_errno_nospace)
2741 goto ret;
2742 }
2743 if (return_status != FILE_RETRY)
2744 goto ret;
2745 }
2746 }
2747
2748 tctx->copied_bytes = tctx->progress_bytes + file_part + ctx->do_reget;
2749
2750 usecs = tv_current - tv_last_update;
2751 update_usecs = tv_current - tv_last_input;
2752
2753 if (is_first_time || usecs > FILEOP_UPDATE_INTERVAL_US)
2754 {
2755 copy_file_file_display_progress (tctx, ctx, tv_current, tv_transfer_start,
2756 file_size, file_part);
2757 tv_last_update = tv_current;
2758 }
2759
2760 is_first_time = FALSE;
2761
2762 if (update_usecs > FILEOP_STALLING_INTERVAL_US)
2763 stalled_msg = _("(stalled)");
2764
2765 force_update = (tv_current - tctx->transfer_start) > FILEOP_UPDATE_INTERVAL_US;
2766
2767 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
2768 {
2769 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
2770 file_progress_show_total (tctx, ctx, tctx->copied_bytes, force_update);
2771 }
2772
2773 file_progress_show (ctx, file_part + ctx->do_reget, file_size, stalled_msg,
2774 force_update);
2775 mc_refresh ();
2776
2777 return_status = check_progress_buttons (ctx);
2778 if (return_status != FILE_CONT)
2779 {
2780 int query_res;
2781
2782 query_res =
2783 query_dialog (Q_ ("DialogTitle|Copy"),
2784 _("Incomplete file was retrieved"), D_ERROR, 3,
2785 _("&Delete"), _("&Keep"), _("&Continue copy"));
2786
2787 switch (query_res)
2788 {
2789 case 0:
2790
2791 dst_status = DEST_SHORT_DELETE;
2792 goto ret;
2793
2794 case 1:
2795
2796 dst_status = DEST_SHORT_KEEP;
2797 goto ret;
2798
2799 default:
2800
2801 break;
2802 }
2803 }
2804 }
2805
2806
2807 dst_status = DEST_FULL;
2808 }
2809
2810 ret:
2811 g_free (buf);
2812
2813 rotate_dash (FALSE);
2814 while (src_desc != -1 && mc_close (src_desc) < 0 && !ctx->skip_all)
2815 {
2816 temp_status = file_error (TRUE, _("Cannot close source file \"%s\"\n%s"), src_path);
2817 if (temp_status == FILE_RETRY)
2818 continue;
2819 if (temp_status == FILE_ABORT)
2820 return_status = temp_status;
2821 if (temp_status == FILE_SKIPALL)
2822 ctx->skip_all = TRUE;
2823 break;
2824 }
2825
2826 while (dest_desc != -1 && mc_close (dest_desc) < 0 && !ctx->skip_all)
2827 {
2828 temp_status = file_error (TRUE, _("Cannot close target file \"%s\"\n%s"), dst_path);
2829 if (temp_status == FILE_RETRY)
2830 continue;
2831 if (temp_status == FILE_SKIPALL)
2832 ctx->skip_all = TRUE;
2833 return_status = temp_status;
2834 break;
2835 }
2836
2837 if (dst_status == DEST_SHORT_QUERY)
2838 {
2839
2840 if (query_dialog (Q_ ("DialogTitle|Copy"), _("Incomplete file was retrieved"),
2841 D_ERROR, 2, _("&Delete"), _("&Keep")) == 0)
2842 dst_status = DEST_SHORT_DELETE;
2843 else
2844 dst_status = DEST_SHORT_KEEP;
2845 }
2846
2847 if (dst_status == DEST_SHORT_DELETE)
2848 mc_unlink (dst_vpath);
2849 else if (dst_status == DEST_FULL && !appending)
2850 {
2851
2852
2853 while (ctx->preserve_uidgid && mc_chown (dst_vpath, src_uid, src_gid) != 0
2854 && !ctx->skip_all)
2855 {
2856 temp_status = file_error (TRUE, _("Cannot chown target file \"%s\"\n%s"), dst_path);
2857 if (temp_status == FILE_RETRY)
2858 continue;
2859 if (temp_status == FILE_SKIPALL)
2860 {
2861 ctx->skip_all = TRUE;
2862 return_status = FILE_CONT;
2863 }
2864 if (temp_status == FILE_SKIP)
2865 return_status = FILE_CONT;
2866 break;
2867 }
2868
2869 while (ctx->preserve && mc_chmod (dst_vpath, (src_mode & ctx->umask_kill)) != 0
2870 && !ctx->skip_all)
2871 {
2872 temp_status = file_error (TRUE, _("Cannot chmod target file \"%s\"\n%s"), dst_path);
2873 if (temp_status == FILE_RETRY)
2874 continue;
2875 if (temp_status == FILE_SKIPALL)
2876 {
2877 ctx->skip_all = TRUE;
2878 return_status = FILE_CONT;
2879 }
2880 if (temp_status == FILE_SKIP)
2881 return_status = FILE_CONT;
2882 break;
2883 }
2884
2885 if (!ctx->preserve && !dst_exists)
2886 {
2887 src_mode = umask (-1);
2888 umask (src_mode);
2889 src_mode = 0100666 & ~src_mode;
2890 mc_chmod (dst_vpath, (src_mode & ctx->umask_kill));
2891 }
2892 }
2893
2894 if (dst_status == DEST_FULL || dst_status == DEST_SHORT_KEEP)
2895 {
2896
2897 mc_utime (dst_vpath, ×);
2898
2899 while (attrs_ok && mc_fsetflags (dst_vpath, attrs) != 0 && !ctx->skip_all)
2900 {
2901 attrs_ok = FALSE;
2902
2903
2904 if (errno == ENOTSUP)
2905 {
2906 return_status = FILE_CONT;
2907 break;
2908 }
2909
2910 temp_status = file_error (TRUE, _("Cannot set attributes for target file \"%s\"\n%s"),
2911 dst_path);
2912 if (temp_status == FILE_RETRY)
2913 {
2914 attrs_ok = TRUE;
2915 continue;
2916 }
2917 if (temp_status == FILE_SKIPALL)
2918 {
2919 ctx->skip_all = TRUE;
2920 return_status = FILE_CONT;
2921 }
2922 if (temp_status == FILE_SKIP)
2923 return_status = FILE_CONT;
2924 break;
2925 }
2926 }
2927
2928 if (return_status == FILE_CONT)
2929 return_status = progress_update_one (tctx, ctx, file_size);
2930
2931 ret_fast:
2932 vfs_path_free (src_vpath, TRUE);
2933 vfs_path_free (dst_vpath, TRUE);
2934 return return_status;
2935 }
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945 FileProgressStatus
2946 copy_dir_dir (file_op_total_context_t * tctx, file_op_context_t * ctx, const char *s, const char *d,
2947 gboolean toplevel, gboolean move_over, gboolean do_delete, GSList * parent_dirs)
2948 {
2949 struct vfs_dirent *next;
2950 struct stat dst_stat, src_stat;
2951 unsigned long attrs;
2952 gboolean attrs_ok = ctx->preserve;
2953 DIR *reading;
2954 FileProgressStatus return_status = FILE_CONT;
2955 struct link *lp;
2956 vfs_path_t *src_vpath, *dst_vpath;
2957 gboolean do_mkdir = TRUE;
2958
2959 src_vpath = vfs_path_from_str (s);
2960 dst_vpath = vfs_path_from_str (d);
2961
2962
2963
2964 retry_src_stat:
2965 if ((*ctx->stat_func) (src_vpath, &src_stat) != 0)
2966 {
2967 if (ctx->skip_all)
2968 return_status = FILE_SKIPALL;
2969 else
2970 {
2971 return_status = file_error (TRUE, _("Cannot stat source directory \"%s\"\n%s"), s);
2972 if (return_status == FILE_RETRY)
2973 goto retry_src_stat;
2974 if (return_status == FILE_SKIPALL)
2975 ctx->skip_all = TRUE;
2976 }
2977 goto ret_fast;
2978 }
2979
2980 while (attrs_ok && mc_fgetflags (src_vpath, &attrs) != 0)
2981 {
2982 attrs_ok = FALSE;
2983
2984
2985 if (errno == ENOTSUP)
2986 {
2987 return_status = FILE_CONT;
2988 break;
2989 }
2990
2991 if (ctx->skip_all)
2992 return_status = FILE_SKIPALL;
2993 else
2994 {
2995 return_status =
2996 file_error (TRUE, _("Cannot get attributes of source directory \"%s\"\n%s"), s);
2997 if (return_status == FILE_RETRY)
2998 {
2999 attrs_ok = TRUE;
3000 continue;
3001 }
3002 if (return_status == FILE_SKIPALL)
3003 ctx->skip_all = TRUE;
3004 }
3005 goto ret_fast;
3006 }
3007
3008 if (is_in_linklist (dest_dirs, src_vpath, &src_stat) != NULL)
3009 {
3010
3011
3012
3013 return_status = FILE_CONT;
3014 goto ret_fast;
3015 }
3016
3017
3018
3019
3020 if (ctx->preserve)
3021 {
3022 switch (check_hardlinks (src_vpath, &src_stat, dst_vpath, &ctx->skip_all))
3023 {
3024 case HARDLINK_OK:
3025
3026 goto ret_fast;
3027
3028 case HARDLINK_ABORT:
3029 return_status = FILE_ABORT;
3030 goto ret_fast;
3031
3032 default:
3033 break;
3034 }
3035 }
3036
3037 if (!S_ISDIR (src_stat.st_mode))
3038 {
3039 if (ctx->skip_all)
3040 return_status = FILE_SKIPALL;
3041 else
3042 {
3043 return_status = file_error (TRUE, _("Source \"%s\" is not a directory\n%s"), s);
3044 if (return_status == FILE_RETRY)
3045 goto retry_src_stat;
3046 if (return_status == FILE_SKIPALL)
3047 ctx->skip_all = TRUE;
3048 }
3049 goto ret_fast;
3050 }
3051
3052 if (is_in_linklist (parent_dirs, src_vpath, &src_stat) != NULL)
3053 {
3054
3055 message (D_ERROR, MSG_ERROR, _("Cannot copy cyclic symbolic link\n\"%s\""), s);
3056 return_status = FILE_SKIP;
3057 goto ret_fast;
3058 }
3059
3060 lp = g_new0 (struct link, 1);
3061 lp->vfs = vfs_path_get_last_path_vfs (src_vpath);
3062 lp->ino = src_stat.st_ino;
3063 lp->dev = src_stat.st_dev;
3064 parent_dirs = g_slist_prepend (parent_dirs, lp);
3065
3066 retry_dst_stat:
3067
3068 if (mc_stat (dst_vpath, &dst_stat) != 0)
3069 {
3070
3071 if (move_over && mc_rename (src_vpath, dst_vpath) == 0)
3072 {
3073 return_status = FILE_CONT;
3074 goto ret;
3075 }
3076 }
3077 else
3078 {
3079
3080
3081
3082
3083
3084
3085
3086
3087 if (!S_ISDIR (dst_stat.st_mode))
3088 {
3089 if (ctx->skip_all)
3090 return_status = FILE_SKIPALL;
3091 else
3092 {
3093 return_status =
3094 file_error (TRUE, _("Destination \"%s\" must be a directory\n%s"), d);
3095 if (return_status == FILE_SKIPALL)
3096 ctx->skip_all = TRUE;
3097 if (return_status == FILE_RETRY)
3098 goto retry_dst_stat;
3099 }
3100 goto ret;
3101 }
3102
3103 if (toplevel && ctx->dive_into_subdirs)
3104 {
3105 vfs_path_t *tmp;
3106
3107 tmp = dst_vpath;
3108 dst_vpath = vfs_path_append_new (dst_vpath, x_basename (s), (char *) NULL);
3109 vfs_path_free (tmp, TRUE);
3110
3111 }
3112 else
3113 do_mkdir = FALSE;
3114 }
3115
3116 d = vfs_path_as_str (dst_vpath);
3117
3118 if (do_mkdir)
3119 {
3120 while (my_mkdir (dst_vpath, (src_stat.st_mode & ctx->umask_kill) | S_IRWXU) != 0)
3121 {
3122 if (ctx->skip_all)
3123 return_status = FILE_SKIPALL;
3124 else
3125 {
3126 return_status =
3127 file_error (TRUE, _("Cannot create target directory \"%s\"\n%s"), d);
3128 if (return_status == FILE_SKIPALL)
3129 ctx->skip_all = TRUE;
3130 }
3131 if (return_status != FILE_RETRY)
3132 goto ret;
3133 }
3134
3135 lp = g_new0 (struct link, 1);
3136 mc_stat (dst_vpath, &dst_stat);
3137 lp->vfs = vfs_path_get_last_path_vfs (dst_vpath);
3138 lp->ino = dst_stat.st_ino;
3139 lp->dev = dst_stat.st_dev;
3140 dest_dirs = g_slist_prepend (dest_dirs, lp);
3141 }
3142
3143 if (ctx->preserve_uidgid)
3144 {
3145 while (mc_chown (dst_vpath, src_stat.st_uid, src_stat.st_gid) != 0)
3146 {
3147 if (ctx->skip_all)
3148 return_status = FILE_SKIPALL;
3149 else
3150 {
3151 return_status = file_error (TRUE, _("Cannot chown target directory \"%s\"\n%s"), d);
3152 if (return_status == FILE_SKIPALL)
3153 ctx->skip_all = TRUE;
3154 }
3155 if (return_status != FILE_RETRY)
3156 goto ret;
3157 }
3158 }
3159
3160
3161 reading = mc_opendir (src_vpath);
3162 if (reading == NULL)
3163 goto ret;
3164
3165 while ((next = mc_readdir (reading)) && return_status != FILE_ABORT)
3166 {
3167 char *path;
3168 vfs_path_t *tmp_vpath;
3169
3170
3171
3172
3173 if (DIR_IS_DOT (next->d_name) || DIR_IS_DOTDOT (next->d_name))
3174 continue;
3175
3176
3177 path = mc_build_filename (s, next->d_name, (char *) NULL);
3178 tmp_vpath = vfs_path_from_str (path);
3179
3180 (*ctx->stat_func) (tmp_vpath, &dst_stat);
3181 if (S_ISDIR (dst_stat.st_mode))
3182 {
3183 char *mdpath;
3184
3185 mdpath = mc_build_filename (d, next->d_name, (char *) NULL);
3186
3187
3188
3189
3190
3191
3192 return_status =
3193 copy_dir_dir (tctx, ctx, path, mdpath, FALSE, FALSE, do_delete, parent_dirs);
3194 g_free (mdpath);
3195 }
3196 else
3197 {
3198 char *dest_file;
3199
3200 dest_file = mc_build_filename (d, x_basename (path), (char *) NULL);
3201 return_status = copy_file_file (tctx, ctx, path, dest_file);
3202 g_free (dest_file);
3203 }
3204
3205 g_free (path);
3206
3207 if (do_delete && return_status == FILE_CONT)
3208 {
3209 if (ctx->erase_at_end)
3210 {
3211 if (erase_list == NULL)
3212 erase_list = g_queue_new ();
3213
3214 lp = g_new0 (struct link, 1);
3215 lp->src_vpath = tmp_vpath;
3216 lp->st_mode = dst_stat.st_mode;
3217 g_queue_push_tail (erase_list, lp);
3218 tmp_vpath = NULL;
3219 }
3220 else if (S_ISDIR (dst_stat.st_mode))
3221 return_status = erase_dir_iff_empty (ctx, tmp_vpath, tctx->progress_count);
3222 else
3223 return_status = erase_file (tctx, ctx, tmp_vpath);
3224 }
3225 vfs_path_free (tmp_vpath, TRUE);
3226 }
3227 mc_closedir (reading);
3228
3229 if (ctx->preserve)
3230 {
3231 mc_timesbuf_t times;
3232
3233 mc_chmod (dst_vpath, src_stat.st_mode & ctx->umask_kill);
3234
3235 if (attrs_ok)
3236 mc_fsetflags (dst_vpath, attrs);
3237
3238 get_times (&src_stat, ×);
3239 mc_utime (dst_vpath, ×);
3240 }
3241 else
3242 {
3243 src_stat.st_mode = umask (-1);
3244 umask (src_stat.st_mode);
3245 src_stat.st_mode = 0100777 & ~src_stat.st_mode;
3246 mc_chmod (dst_vpath, src_stat.st_mode & ctx->umask_kill);
3247 }
3248
3249 ret:
3250 free_link (parent_dirs->data);
3251 g_slist_free_1 (parent_dirs);
3252 ret_fast:
3253 vfs_path_free (src_vpath, TRUE);
3254 vfs_path_free (dst_vpath, TRUE);
3255 return return_status;
3256 }
3257
3258
3259
3260
3261
3262
3263 FileProgressStatus
3264 move_dir_dir (file_op_total_context_t * tctx, file_op_context_t * ctx, const char *s, const char *d)
3265 {
3266 return do_move_dir_dir (NULL, tctx, ctx, s, d);
3267 }
3268
3269
3270
3271
3272
3273
3274 FileProgressStatus
3275 erase_dir (file_op_total_context_t * tctx, file_op_context_t * ctx, const vfs_path_t * vpath)
3276 {
3277 file_progress_show_deleting (ctx, vfs_path_as_str (vpath), NULL);
3278 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
3279 if (check_progress_buttons (ctx) == FILE_ABORT)
3280 return FILE_ABORT;
3281
3282 mc_refresh ();
3283
3284
3285
3286
3287
3288
3289
3290
3291 if (check_dir_is_empty (vpath) == 0)
3292 {
3293 FileProgressStatus error;
3294
3295 error = query_recursive (ctx, vfs_path_as_str (vpath));
3296 if (error == FILE_CONT)
3297 error = recursive_erase (tctx, ctx, vpath);
3298 return error;
3299 }
3300
3301 return try_erase_dir (ctx, vfs_path_as_str (vpath));
3302 }
3303
3304
3305
3306
3307
3308
3309 void
3310 dirsize_status_init_cb (status_msg_t * sm)
3311 {
3312 dirsize_status_msg_t *dsm = (dirsize_status_msg_t *) sm;
3313 WGroup *gd = GROUP (sm->dlg);
3314 Widget *wd = WIDGET (sm->dlg);
3315 WRect r = wd->rect;
3316
3317 const char *b1_name = N_("&Abort");
3318 const char *b2_name = N_("&Skip");
3319 int b_width, ui_width;
3320
3321 #ifdef ENABLE_NLS
3322 b1_name = _(b1_name);
3323 b2_name = _(b2_name);
3324 #endif
3325
3326 b_width = str_term_width1 (b1_name) + 4;
3327 if (dsm->allow_skip)
3328 b_width += str_term_width1 (b2_name) + 4 + 1;
3329
3330 ui_width = MAX (COLS / 2, b_width + 6);
3331 dsm->dirname = label_new (2, 3, NULL);
3332 group_add_widget (gd, dsm->dirname);
3333 dsm->count_size = label_new (3, 3, NULL);
3334 group_add_widget (gd, dsm->count_size);
3335 group_add_widget (gd, hline_new (4, -1, -1));
3336
3337 dsm->abort_button = WIDGET (button_new (5, 3, FILE_ABORT, NORMAL_BUTTON, b1_name, NULL));
3338 group_add_widget (gd, dsm->abort_button);
3339 if (dsm->allow_skip)
3340 {
3341 dsm->skip_button = WIDGET (button_new (5, 3, FILE_SKIP, NORMAL_BUTTON, b2_name, NULL));
3342 group_add_widget (gd, dsm->skip_button);
3343 widget_select (dsm->skip_button);
3344 }
3345
3346 r.lines = 8;
3347 r.cols = ui_width;
3348 widget_set_size_rect (wd, &r);
3349 dirsize_status_locate_buttons (dsm);
3350 }
3351
3352
3353
3354 int
3355 dirsize_status_update_cb (status_msg_t * sm)
3356 {
3357 dirsize_status_msg_t *dsm = (dirsize_status_msg_t *) sm;
3358 Widget *wd = WIDGET (sm->dlg);
3359 WRect r = wd->rect;
3360
3361
3362 label_set_textv (dsm->count_size, _("Directories: %zu, total size: %s"),
3363 dsm->dir_count, size_trunc_sep (dsm->total_size, panels_options.kilobyte_si));
3364
3365
3366 if (WIDGET (dsm->count_size)->rect.cols + 6 > r.cols)
3367 {
3368 r.cols = WIDGET (dsm->count_size)->rect.cols + 6;
3369 widget_set_size_rect (wd, &r);
3370 dirsize_status_locate_buttons (dsm);
3371 widget_draw (wd);
3372
3373 }
3374
3375
3376 label_set_text (dsm->dirname,
3377 str_trunc (vfs_path_as_str (dsm->dirname_vpath), wd->rect.cols - 6));
3378
3379 switch (status_msg_common_update (sm))
3380 {
3381 case B_CANCEL:
3382 case FILE_ABORT:
3383 return FILE_ABORT;
3384 case FILE_SKIP:
3385 return FILE_SKIP;
3386 default:
3387 return FILE_CONT;
3388 }
3389 }
3390
3391
3392
3393 void
3394 dirsize_status_deinit_cb (status_msg_t * sm)
3395 {
3396 (void) sm;
3397
3398
3399 if (get_other_type () == view_listing)
3400 other_panel->dirty = TRUE;
3401 }
3402
3403
3404
3405
3406
3407
3408
3409
3410 FileProgressStatus
3411 compute_dir_size (const vfs_path_t * dirname_vpath, dirsize_status_msg_t * sm,
3412 size_t * ret_dir_count, size_t * ret_marked_count, uintmax_t * ret_total,
3413 gboolean follow_symlinks)
3414 {
3415 return do_compute_dir_size (dirname_vpath, sm, ret_dir_count, ret_marked_count, ret_total,
3416 follow_symlinks ? mc_stat : mc_lstat);
3417 }
3418
3419
3420
3421
3422
3423
3424
3425
3426
3427
3428
3429
3430
3431
3432
3433 gboolean
3434 panel_operate (void *source_panel, FileOperation operation, gboolean force_single)
3435 {
3436 WPanel *panel = PANEL (source_panel);
3437 const gboolean single_entry = force_single || (panel->marked <= 1)
3438 || (get_current_type () == view_tree);
3439
3440 const char *source = NULL;
3441 char *dest = NULL;
3442 vfs_path_t *dest_vpath = NULL;
3443 vfs_path_t *save_cwd = NULL, *save_dest = NULL;
3444 struct stat src_stat;
3445 gboolean ret_val = TRUE;
3446 int i;
3447 FileProgressStatus value;
3448 file_op_context_t *ctx;
3449 file_op_total_context_t *tctx;
3450 filegui_dialog_type_t dialog_type = FILEGUI_DIALOG_ONE_ITEM;
3451
3452 gboolean do_bg = FALSE;
3453
3454 static gboolean i18n_flag = FALSE;
3455 if (!i18n_flag)
3456 {
3457 for (i = G_N_ELEMENTS (op_names); i-- != 0;)
3458 op_names[i] = Q_ (op_names[i]);
3459 i18n_flag = TRUE;
3460 }
3461
3462 linklist = free_linklist (linklist);
3463 dest_dirs = free_linklist (dest_dirs);
3464
3465 save_cwds_stat ();
3466
3467 if (single_entry)
3468 {
3469 source = check_single_entry (panel, force_single, &src_stat);
3470
3471 if (source == NULL)
3472 return FALSE;
3473 }
3474
3475 ctx = file_op_context_new (operation);
3476
3477
3478 if (operation != OP_DELETE)
3479 {
3480 dest = do_confirm_copy_move (panel, force_single, source, &src_stat, ctx, &do_bg);
3481 if (dest == NULL)
3482 {
3483 ret_val = FALSE;
3484 goto ret_fast;
3485 }
3486
3487 dest_vpath = vfs_path_from_str (dest);
3488 }
3489 else if (confirm_delete && !do_confirm_erase (panel, source, &src_stat))
3490 {
3491 ret_val = FALSE;
3492 goto ret_fast;
3493 }
3494
3495 tctx = file_op_total_context_new ();
3496 tctx->transfer_start = g_get_monotonic_time ();
3497
3498 #ifdef ENABLE_BACKGROUND
3499
3500 if (do_bg)
3501 {
3502 int v;
3503
3504 v = do_background (ctx,
3505 g_strconcat (op_names[operation], ": ",
3506 vfs_path_as_str (panel->cwd_vpath), (char *) NULL));
3507 if (v == -1)
3508 message (D_ERROR, MSG_ERROR, _("Sorry, I could not put the job in background"));
3509
3510
3511 if (v == 1)
3512 {
3513 mc_setctl (panel->cwd_vpath, VFS_SETCTL_FORGET, NULL);
3514
3515 mc_setctl (dest_vpath, VFS_SETCTL_FORGET, NULL);
3516 vfs_path_free (dest_vpath, TRUE);
3517 g_free (dest);
3518
3519 return FALSE;
3520 }
3521 }
3522 else
3523 #endif
3524 {
3525 if (operation == OP_DELETE)
3526 dialog_type = FILEGUI_DIALOG_DELETE_ITEM;
3527 else if (single_entry && S_ISDIR (panel_current_entry (panel)->st.st_mode))
3528 dialog_type = FILEGUI_DIALOG_MULTI_ITEM;
3529 else if (single_entry || force_single)
3530 dialog_type = FILEGUI_DIALOG_ONE_ITEM;
3531 else
3532 dialog_type = FILEGUI_DIALOG_MULTI_ITEM;
3533 }
3534
3535
3536
3537
3538
3539 if ((dest != NULL)
3540 && (mc_setctl (dest_vpath, VFS_SETCTL_STALE_DATA, GUINT_TO_POINTER (1)) != 0))
3541 save_dest = vfs_path_from_str (dest);
3542
3543 if ((vfs_path_tokens_count (panel->cwd_vpath) != 0)
3544 && (mc_setctl (panel->cwd_vpath, VFS_SETCTL_STALE_DATA, GUINT_TO_POINTER (1)) != 0))
3545 save_cwd = vfs_path_clone (panel->cwd_vpath);
3546
3547
3548
3549
3550 if (single_entry)
3551 {
3552
3553
3554
3555 if ((operation != OP_COPY) && (get_current_type () == view_tree))
3556 {
3557 vfs_path_t *vpath;
3558 int chdir_retcode;
3559
3560 vpath = vfs_path_from_str (PATH_SEP_STR);
3561 chdir_retcode = mc_chdir (vpath);
3562 vfs_path_free (vpath, TRUE);
3563 if (chdir_retcode < 0)
3564 {
3565 ret_val = FALSE;
3566 goto clean_up;
3567 }
3568 }
3569
3570 value = operate_single_file (panel, tctx, ctx, source, &src_stat, dest, dialog_type);
3571 if ((value == FILE_CONT) && !force_single)
3572 unmark_files (panel);
3573 }
3574 else
3575 {
3576
3577
3578
3579 while (operation != OP_DELETE)
3580 {
3581 int dst_result;
3582 struct stat dst_stat;
3583
3584 dst_result = mc_stat (dest_vpath, &dst_stat);
3585
3586 if ((dst_result != 0) || S_ISDIR (dst_stat.st_mode))
3587 break;
3588
3589 if (ctx->skip_all
3590 || file_error (TRUE, _("Destination \"%s\" must be a directory\n%s"),
3591 dest) != FILE_RETRY)
3592 goto clean_up;
3593 }
3594
3595
3596
3597
3598
3599
3600 if (panel_operate_init_totals (panel, NULL, NULL, ctx, file_op_compute_totals, dialog_type)
3601 == FILE_CONT)
3602 {
3603
3604 for (i = 0; i < panel->dir.len; i++)
3605 {
3606 const char *source2;
3607
3608 if (panel->dir.list[i].f.marked == 0)
3609 continue;
3610
3611 source2 = panel->dir.list[i].fname->str;
3612 src_stat = panel->dir.list[i].st;
3613
3614 value = operate_one_file (panel, tctx, ctx, source2, &src_stat, dest);
3615
3616 if (value == FILE_ABORT)
3617 break;
3618
3619 if (value == FILE_CONT)
3620 do_file_mark (panel, i, 0);
3621
3622 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
3623 {
3624 file_progress_show_count (ctx, tctx->progress_count, ctx->progress_count);
3625 file_progress_show_total (tctx, ctx, tctx->progress_bytes, FALSE);
3626 }
3627
3628 if (operation != OP_DELETE)
3629 file_progress_show (ctx, 0, 0, "", FALSE);
3630
3631 if (check_progress_buttons (ctx) == FILE_ABORT)
3632 break;
3633
3634 mc_refresh ();
3635 }
3636 }
3637 }
3638
3639 clean_up:
3640
3641 if (save_cwd != NULL)
3642 {
3643 mc_setctl (save_cwd, VFS_SETCTL_STALE_DATA, NULL);
3644 vfs_path_free (save_cwd, TRUE);
3645 }
3646
3647 if (save_dest != NULL)
3648 {
3649 mc_setctl (save_dest, VFS_SETCTL_STALE_DATA, NULL);
3650 vfs_path_free (save_dest, TRUE);
3651 }
3652
3653 linklist = free_linklist (linklist);
3654 dest_dirs = free_linklist (dest_dirs);
3655 g_free (dest);
3656 vfs_path_free (dest_vpath, TRUE);
3657 MC_PTR_FREE (ctx->dest_mask);
3658
3659 #ifdef ENABLE_BACKGROUND
3660
3661 if (mc_global.we_are_background)
3662 {
3663 int cur_pid = getpid ();
3664
3665
3666 ctx->pid = cur_pid;
3667 parent_call ((void *) end_bg_process, ctx, 0);
3668
3669 vfs_shut ();
3670 my_exit (EXIT_SUCCESS);
3671 }
3672 #endif
3673
3674 file_op_total_context_destroy (tctx);
3675 ret_fast:
3676 file_op_context_destroy (ctx);
3677
3678 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
3679 repaint_screen ();
3680
3681 return ret_val;
3682 }
3683
3684
3685
3686
3687
3688
3689 FileProgressStatus
3690 file_error (gboolean allow_retry, const char *format, const char *file)
3691 {
3692 char buf[BUF_MEDIUM];
3693
3694 g_snprintf (buf, sizeof (buf), format, path_trunc (file, 30), unix_error_string (errno));
3695
3696 return do_file_error (allow_retry, buf);
3697 }
3698
3699
3700
3701
3702
3703
3704
3705