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