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