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
78
79 #include "dir.h"
80 #include "filenot.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,
404 _ ("Cannot stat hardlink source file \"%s\"\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 =
424 file_error (ctx, TRUE, _ ("Cannot create target hardlink \"%s\"\n%s"),
425 vfs_path_as_str (dst_vpath));
426 if (status == FILE_ABORT)
427 return HARDLINK_ABORT;
428 if (status == FILE_RETRY)
429 continue;
430 if (status == FILE_IGNORE_ALL)
431 *ignore_all = TRUE;
432 break;
433 }
434
435
436 return (ok ? HARDLINK_OK : HARDLINK_ERROR);
437 }
438 }
439 }
440
441 if (!*ignore_all)
442 {
443 FileProgressStatus status;
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458 errno = 0;
459 status = file_error (ctx, FALSE, _ ("Cannot create target hardlink \"%s\""),
460 vfs_path_as_str (dst_vpath));
461
462 if (status == FILE_ABORT)
463 return HARDLINK_ABORT;
464
465 if (status == FILE_IGNORE_ALL)
466 *ignore_all = TRUE;
467 }
468
469 return HARDLINK_ERROR;
470 }
471
472 lnk = g_try_new (link_t, 1);
473 if (lnk != NULL)
474 {
475 lnk->vfs = vfs_path_get_last_path_vfs (src_vpath);
476 lnk->ino = ino;
477 lnk->dev = dev;
478 lnk->st_mode = 0;
479 lnk->src_vpath = vfs_path_clone (src_vpath);
480 lnk->dst_vpath = vfs_path_clone (dst_vpath);
481
482 linklist = g_slist_prepend (linklist, lnk);
483 }
484
485 return HARDLINK_CACHED;
486 }
487
488
489
490
491
492
493
494
495
496
497 static FileProgressStatus
498 make_symlink (file_op_context_t *ctx, const vfs_path_t *src_vpath, const vfs_path_t *dst_vpath)
499 {
500 const char *src_path;
501 const char *dst_path;
502 char link_target[MC_MAXPATHLEN];
503 int len;
504 FileProgressStatus return_status;
505 struct stat dst_stat;
506 gboolean dst_is_symlink;
507 vfs_path_t *link_target_vpath = NULL;
508
509 src_path = vfs_path_as_str (src_vpath);
510 dst_path = vfs_path_as_str (dst_vpath);
511
512 dst_is_symlink = (mc_lstat (dst_vpath, &dst_stat) == 0) && S_ISLNK (dst_stat.st_mode);
513
514 retry_src_readlink:
515 len = mc_readlink (src_vpath, link_target, sizeof (link_target) - 1);
516 if (len < 0)
517 {
518 if (ctx->ignore_all)
519 return_status = FILE_IGNORE_ALL;
520 else
521 {
522 return_status =
523 file_error (ctx, TRUE, _ ("Cannot read source link \"%s\"\n%s"), src_path);
524 if (return_status == FILE_IGNORE_ALL)
525 ctx->ignore_all = TRUE;
526 if (return_status == FILE_RETRY)
527 goto retry_src_readlink;
528 }
529 goto ret;
530 }
531
532 link_target[len] = '\0';
533
534 if (ctx->stable_symlinks && !(vfs_file_is_local (src_vpath) && vfs_file_is_local (dst_vpath)))
535 {
536 message (D_ERROR, MSG_ERROR,
537 _ ("Cannot make stable symlinks across "
538 "non-local filesystems:\n\nOption Stable Symlinks will be disabled"));
539 ctx->stable_symlinks = FALSE;
540 }
541
542 if (ctx->stable_symlinks && !g_path_is_absolute (link_target))
543 {
544 const char *r;
545
546 r = strrchr (src_path, PATH_SEP);
547 if (r != NULL)
548 {
549 size_t slen;
550 GString *p;
551 vfs_path_t *q;
552
553 slen = r - src_path + 1;
554
555 p = g_string_sized_new (slen + len);
556 g_string_append_len (p, src_path, slen);
557
558 if (g_path_is_absolute (dst_path))
559 q = vfs_path_from_str_flags (dst_path, VPF_NO_CANON);
560 else
561 q = vfs_path_build_filename (p->str, dst_path, (char *) NULL);
562
563 if (vfs_path_tokens_count (q) > 1)
564 {
565 char *s = NULL;
566 vfs_path_t *tmp_vpath1, *tmp_vpath2;
567
568 g_string_append_len (p, link_target, len);
569 tmp_vpath1 = vfs_path_vtokens_get (q, -1, 1);
570 tmp_vpath2 = vfs_path_from_str (p->str);
571 s = diff_two_paths (tmp_vpath1, tmp_vpath2);
572 vfs_path_free (tmp_vpath2, TRUE);
573 vfs_path_free (tmp_vpath1, TRUE);
574 g_strlcpy (link_target, s != NULL ? s : p->str, sizeof (link_target));
575 g_free (s);
576 }
577
578 g_string_free (p, TRUE);
579 vfs_path_free (q, TRUE);
580 }
581 }
582 link_target_vpath = vfs_path_from_str_flags (link_target, VPF_NO_CANON);
583
584 retry_dst_symlink:
585 if (mc_symlink (link_target_vpath, dst_vpath) == 0)
586 {
587
588 return_status = FILE_CONT;
589 goto ret;
590 }
591
592
593
594
595 if (dst_is_symlink && mc_unlink (dst_vpath) == 0
596 && mc_symlink (link_target_vpath, dst_vpath) == 0)
597 {
598
599 return_status = FILE_CONT;
600 goto ret;
601 }
602
603 if (ctx->ignore_all)
604 return_status = FILE_IGNORE_ALL;
605 else
606 {
607 return_status =
608 file_error (ctx, TRUE, _ ("Cannot create target symlink \"%s\"\n%s"), dst_path);
609 if (return_status == FILE_IGNORE_ALL)
610 ctx->ignore_all = TRUE;
611 if (return_status == FILE_RETRY)
612 goto retry_dst_symlink;
613 }
614
615 ret:
616 vfs_path_free (link_target_vpath, TRUE);
617 return return_status;
618 }
619
620
621
622
623
624
625
626
627 static FileProgressStatus
628 do_compute_dir_size (const vfs_path_t *dirname_vpath, dirsize_status_msg_t *dsm, size_t *dir_count,
629 size_t *ret_marked, uintmax_t *ret_total, mc_stat_fn stat_func)
630 {
631 static gint64 timestamp = 0;
632
633 static const gint64 delay = G_USEC_PER_SEC / 25;
634
635 status_msg_t *sm = STATUS_MSG (dsm);
636 int res;
637 struct stat s;
638 DIR *dir;
639 struct vfs_dirent *dirent;
640 FileProgressStatus ret = FILE_CONT;
641
642 (*dir_count)++;
643
644 dir = mc_opendir (dirname_vpath);
645 if (dir == NULL)
646 return ret;
647
648 while (ret == FILE_CONT && (dirent = mc_readdir (dir)) != NULL)
649 {
650 vfs_path_t *tmp_vpath;
651
652 if (DIR_IS_DOT (dirent->d_name) || DIR_IS_DOTDOT (dirent->d_name))
653 continue;
654
655 tmp_vpath = vfs_path_append_new (dirname_vpath, dirent->d_name, (char *) NULL);
656
657 res = stat_func (tmp_vpath, &s);
658 if (res == 0)
659 {
660 if (S_ISDIR (s.st_mode))
661 ret = do_compute_dir_size (tmp_vpath, dsm, dir_count, ret_marked, ret_total,
662 stat_func);
663 else
664 {
665 ret = FILE_CONT;
666
667 (*ret_marked)++;
668 *ret_total += (uintmax_t) s.st_size;
669 }
670
671 if (ret == FILE_CONT && sm->update != NULL && mc_time_elapsed (×tamp, delay))
672 {
673 dsm->dirname_vpath = tmp_vpath;
674 dsm->dir_count = *dir_count;
675 dsm->total_size = *ret_total;
676 ret = sm->update (sm);
677 }
678 }
679
680 vfs_path_free (tmp_vpath, TRUE);
681 }
682
683 mc_closedir (dir);
684 return ret;
685 }
686
687
688
689
690
691
692
693
694
695
696
697 static FileProgressStatus
698 panel_compute_totals (const WPanel *panel, dirsize_status_msg_t *sm, size_t *ret_count,
699 uintmax_t *ret_total, gboolean follow_symlinks)
700 {
701 int i;
702 size_t dir_count = 0;
703 mc_stat_fn stat_func = follow_symlinks ? mc_stat : mc_lstat;
704
705 for (i = 0; i < panel->dir.len; i++)
706 {
707 const file_entry_t *fe = &panel->dir.list[i];
708 const struct stat *s;
709
710 if (fe->f.marked == 0)
711 continue;
712
713 s = &fe->st;
714
715 if (S_ISDIR (s->st_mode) || (follow_symlinks && link_isdir (fe) && fe->f.stale_link == 0))
716 {
717 vfs_path_t *p;
718 FileProgressStatus status;
719
720 p = vfs_path_append_new (panel->cwd_vpath, fe->fname->str, (char *) NULL);
721 status = do_compute_dir_size (p, sm, &dir_count, ret_count, ret_total, stat_func);
722 vfs_path_free (p, TRUE);
723
724 if (status != FILE_CONT)
725 return status;
726 }
727 else
728 {
729 (*ret_count)++;
730 *ret_total += (uintmax_t) s->st_size;
731 }
732 }
733
734 return FILE_CONT;
735 }
736
737
738
739
740 static FileProgressStatus
741 panel_operate_init_totals (const WPanel *panel, const vfs_path_t *source,
742 const struct stat *source_stat, file_op_context_t *ctx,
743 gboolean compute_totals, filegui_dialog_type_t dialog_type)
744 {
745 FileProgressStatus status;
746
747 #ifdef ENABLE_BACKGROUND
748 if (mc_global.we_are_background)
749 return FILE_CONT;
750 #endif
751
752 if (verbose && compute_totals)
753 {
754 dirsize_status_msg_t dsm;
755 gboolean stale_link = FALSE;
756
757 memset (&dsm, 0, sizeof (dsm));
758 dsm.allow_skip = TRUE;
759 status_msg_init (STATUS_MSG (&dsm), _ ("Directory scanning"), 0, dirsize_status_init_cb,
760 dirsize_status_update_cb, dirsize_status_deinit_cb);
761
762 ctx->total_count = 0;
763 ctx->total_bytes = 0;
764
765 if (source == NULL)
766 status = panel_compute_totals (panel, &dsm, &ctx->total_count, &ctx->total_bytes,
767 ctx->follow_links);
768 else if (S_ISDIR (source_stat->st_mode)
769 || (ctx->follow_links
770 && file_is_symlink_to_dir (source, (struct stat *) source_stat, &stale_link)
771 && !stale_link))
772 {
773 size_t dir_count = 0;
774
775 status = do_compute_dir_size (source, &dsm, &dir_count, &ctx->total_count,
776 &ctx->total_bytes, ctx->stat_func);
777 }
778 else
779 {
780 ctx->total_count++;
781 ctx->total_bytes += (uintmax_t) source_stat->st_size;
782 status = FILE_CONT;
783 }
784
785 status_msg_deinit (STATUS_MSG (&dsm));
786
787 ctx->totals_computed = (status == FILE_CONT);
788
789 if (status == FILE_SKIP)
790 status = FILE_CONT;
791 }
792 else
793 {
794 status = FILE_CONT;
795 ctx->total_count = panel->marked;
796 ctx->total_bytes = panel->total;
797 ctx->totals_computed = verbose && dialog_type == FILEGUI_DIALOG_ONE_ITEM;
798 }
799
800
801 file_progress_ui_destroy (ctx);
802
803 file_progress_ui_create (ctx, TRUE, dialog_type);
804
805 return status;
806 }
807
808
809
810 static void
811 progress_update_one (gboolean success, file_op_context_t *ctx, off_t add)
812 {
813 gint64 tv_current;
814 static gint64 tv_start = -1;
815
816 ctx->total_progress_count++;
817 ctx->total_progress_bytes += (uintmax_t) add;
818
819 if (!success)
820 return;
821
822 tv_current = g_get_monotonic_time ();
823
824 if (tv_start < 0)
825 tv_start = tv_current;
826 else if (tv_current - tv_start > FILEOP_UPDATE_INTERVAL_US)
827 {
828 if (verbose && ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
829 {
830 file_progress_show_count (ctx);
831 file_progress_show_total (ctx, ctx->total_progress_bytes, tv_current, TRUE);
832 }
833
834 tv_start = tv_current;
835 }
836 }
837
838
839
840 static FileProgressStatus
841 real_warn_same_file (file_op_context_t *ctx, enum OperationMode mode, const char *fmt,
842 const char *a, const char *b)
843 {
844 char *msg;
845 int result = 0;
846 const char *head_msg;
847 int width_a, width_b, width;
848
849 const gint64 t = g_get_monotonic_time ();
850
851 head_msg = mode == Foreground ? MSG_ERROR : _ ("Background process error");
852
853 width_a = str_term_width1 (a);
854 width_b = str_term_width1 (b);
855 width = COLS - 8;
856
857 if (width_a > width)
858 {
859 if (width_b > width)
860 {
861 char *s;
862
863 s = g_strndup (str_trunc (a, width), width);
864 b = str_trunc (b, width);
865 msg = g_strdup_printf (fmt, s, b);
866 g_free (s);
867 }
868 else
869 {
870 a = str_trunc (a, width);
871 msg = g_strdup_printf (fmt, a, b);
872 }
873 }
874 else
875 {
876 if (width_b > width)
877 b = str_trunc (b, width);
878
879 msg = g_strdup_printf (fmt, a, b);
880 }
881
882 result = query_dialog (head_msg, msg, D_ERROR, 2, _ ("&Skip"), _ ("&Abort"));
883 g_free (msg);
884 do_refresh ();
885
886 ctx->pauses += g_get_monotonic_time () - t;
887
888 return (result == 1) ? FILE_ABORT : FILE_SKIP;
889 }
890
891
892
893 static FileProgressStatus
894 warn_same_file (file_op_context_t *ctx, const char *fmt, const char *a, const char *b)
895 {
896 #ifdef ENABLE_BACKGROUND
897 union
898 {
899 void *p;
900 FileProgressStatus (*f) (file_op_context_t *ctx, enum OperationMode, const char *fmt,
901 const char *a, const char *b);
902 } pntr;
903
904 pntr.f = real_warn_same_file;
905
906 if (mc_global.we_are_background)
907 return parent_call (pntr.p, ctx, 3, strlen (fmt), fmt, strlen (a), a, strlen (b), b);
908 #endif
909 return real_warn_same_file (ctx, Foreground, fmt, a, b);
910 }
911
912
913
914 static gboolean
915 check_same_file (file_op_context_t *ctx, const char *a, const struct stat *ast, const char *b,
916 const struct stat *bst, FileProgressStatus *status)
917 {
918 if (ast->st_dev != bst->st_dev || ast->st_ino != bst->st_ino)
919 return FALSE;
920
921 if (S_ISDIR (ast->st_mode))
922 *status = warn_same_file (ctx, _ ("\"%s\"\nand\n\"%s\"\nare the same directory"), a, b);
923 else
924 *status = warn_same_file (ctx, _ ("\"%s\"\nand\n\"%s\"\nare the same file"), a, b);
925
926 return TRUE;
927 }
928
929
930
931
932 static FileProgressStatus
933 real_do_file_error (file_op_context_t *ctx, enum OperationMode mode, gboolean allow_retry,
934 const char *error)
935 {
936 gint64 t = 0;
937 int result;
938 const char *msg;
939
940 if (ctx != NULL)
941 t = g_get_monotonic_time ();
942
943 msg = mode == Foreground ? MSG_ERROR : _ ("Background process error");
944
945 if (allow_retry)
946 result = query_dialog (msg, error, D_ERROR, 4, _ ("&Ignore"), _ ("Ignore a&ll"),
947 _ ("&Retry"), _ ("&Abort"));
948 else
949 result =
950 query_dialog (msg, error, D_ERROR, 3, _ ("&Ignore"), _ ("Ignore a&ll"), _ ("&Abort"));
951
952 if (ctx != NULL)
953 ctx->pauses += g_get_monotonic_time () - t;
954
955 switch (result)
956 {
957 case 0:
958 do_refresh ();
959 return FILE_IGNORE;
960
961 case 1:
962 do_refresh ();
963 return FILE_IGNORE_ALL;
964
965 case 2:
966 if (allow_retry)
967 {
968 do_refresh ();
969 return FILE_RETRY;
970 }
971 MC_FALLTHROUGH;
972
973 case 3:
974 default:
975 return FILE_ABORT;
976 }
977 }
978
979
980
981 static FileProgressStatus
982 real_query_recursive (file_op_context_t *ctx, enum OperationMode mode, const char *s)
983 {
984 if (ctx->recursive_result < RECURSIVE_ALWAYS)
985 {
986 const char *msg;
987 char *text;
988
989 const gint64 t = g_get_monotonic_time ();
990
991 msg = mode == Foreground
992 ? _ ("Directory \"%s\" not empty.\nDelete it recursively?")
993 : _ ("Background process:\nDirectory \"%s\" not empty.\nDelete it recursively?");
994 text = g_strdup_printf (msg, path_trunc (s, 30));
995
996 if (safe_delete)
997 query_set_sel (1);
998
999 ctx->recursive_result = query_dialog (op_names[OP_DELETE], text, D_ERROR, 5, _ ("&Yes"),
1000 _ ("&No"), _ ("A&ll"), _ ("Non&e"), _ ("&Abort"));
1001 g_free (text);
1002
1003 if (ctx->recursive_result != RECURSIVE_ABORT)
1004 do_refresh ();
1005
1006 ctx->pauses += g_get_monotonic_time () - t;
1007 }
1008
1009 switch (ctx->recursive_result)
1010 {
1011 case RECURSIVE_YES:
1012 case RECURSIVE_ALWAYS:
1013 return FILE_CONT;
1014
1015 case RECURSIVE_NO:
1016 case RECURSIVE_NEVER:
1017 return FILE_SKIP;
1018
1019 case RECURSIVE_ABORT:
1020 default:
1021 return FILE_ABORT;
1022 }
1023 }
1024
1025
1026
1027 #ifdef ENABLE_BACKGROUND
1028 static FileProgressStatus
1029 do_file_error (file_op_context_t *ctx, gboolean allow_retry, const char *str)
1030 {
1031 union
1032 {
1033 void *p;
1034 FileProgressStatus (*f) (file_op_context_t *ctx, enum OperationMode, gboolean,
1035 const char *);
1036 } pntr;
1037
1038 pntr.f = real_do_file_error;
1039
1040 if (mc_global.we_are_background)
1041 return parent_call (pntr.p, ctx, 2, sizeof (allow_retry), allow_retry, strlen (str), str);
1042 else
1043 return real_do_file_error (ctx, Foreground, allow_retry, str);
1044 }
1045
1046
1047
1048 static FileProgressStatus
1049 query_recursive (file_op_context_t *ctx, const char *s)
1050 {
1051 union
1052 {
1053 void *p;
1054 FileProgressStatus (*f) (file_op_context_t *, enum OperationMode, const char *);
1055 } pntr;
1056
1057 pntr.f = real_query_recursive;
1058
1059 if (mc_global.we_are_background)
1060 return parent_call (pntr.p, ctx, 1, strlen (s), s);
1061 else
1062 return real_query_recursive (ctx, Foreground, s);
1063 }
1064
1065
1066
1067 static FileProgressStatus
1068 query_replace (file_op_context_t *ctx, const char *src, struct stat *src_stat, const char *dst,
1069 struct stat *dst_stat)
1070 {
1071 union
1072 {
1073 void *p;
1074 FileProgressStatus (*f) (file_op_context_t *, enum OperationMode, const char *,
1075 struct stat *, const char *, struct stat *);
1076 } pntr;
1077
1078 pntr.f = file_progress_real_query_replace;
1079
1080 if (mc_global.we_are_background)
1081 return parent_call (pntr.p, ctx, 4, strlen (src), src, sizeof (struct stat), src_stat,
1082 strlen (dst), dst, sizeof (struct stat), dst_stat);
1083 else
1084 return file_progress_real_query_replace (ctx, Foreground, src, src_stat, dst, dst_stat);
1085 }
1086
1087 #else
1088
1089
1090 static FileProgressStatus
1091 do_file_error (file_op_context_t *ctx, gboolean allow_retry, const char *str)
1092 {
1093 return real_do_file_error (ctx, Foreground, allow_retry, str);
1094 }
1095
1096
1097
1098 static FileProgressStatus
1099 query_recursive (file_op_context_t *ctx, const char *s)
1100 {
1101 return real_query_recursive (ctx, Foreground, s);
1102 }
1103
1104
1105
1106 static FileProgressStatus
1107 query_replace (file_op_context_t *ctx, const char *src, struct stat *src_stat, const char *dst,
1108 struct stat *dst_stat)
1109 {
1110 return file_progress_real_query_replace (ctx, Foreground, src, src_stat, dst, dst_stat);
1111 }
1112
1113 #endif
1114
1115
1116
1117
1118 static FileProgressStatus
1119 files_error (file_op_context_t *ctx, const char *format, const char *file1, const char *file2)
1120 {
1121 char buf[BUF_MEDIUM];
1122 char *nfile1, *nfile2;
1123
1124 nfile1 = g_strdup (path_trunc (file1, 15));
1125 nfile2 = g_strdup (path_trunc (file2, 15));
1126 g_snprintf (buf, sizeof (buf), format, nfile1, nfile2, unix_error_string (errno));
1127 g_free (nfile1);
1128 g_free (nfile2);
1129
1130 return do_file_error (ctx, TRUE, buf);
1131 }
1132
1133
1134
1135
1136
1137 static void
1138 calc_copy_file_progress (file_op_context_t *ctx, gint64 tv_current, off_t file_part,
1139 off_t file_size)
1140 {
1141 double dt;
1142
1143
1144 rotate_dash (TRUE);
1145
1146
1147 dt = (tv_current - ctx->pauses - ctx->transfer_start) / (double) G_USEC_PER_SEC;
1148
1149 if (file_part == 0)
1150 ctx->eta_secs = 0.0;
1151 else
1152 ctx->eta_secs = ((double) file_size / file_part - 1) * dt;
1153
1154
1155 dt = MAX (1.0, dt);
1156 ctx->bps = (long) (file_part / dt);
1157
1158
1159 if (ctx->total_bytes != 0)
1160 {
1161 dt = (tv_current - ctx->pauses - ctx->total_transfer_start) / (double) G_USEC_PER_SEC;
1162
1163 const uintmax_t copied_bytes = ctx->total_progress_bytes + file_part;
1164 if (copied_bytes == 0)
1165 ctx->total_eta_secs = 0;
1166 else
1167 ctx->total_eta_secs = ((double) ctx->total_bytes / copied_bytes - 1) * dt;
1168
1169 dt = MAX (1.0, dt);
1170 ctx->total_bps = (long) (copied_bytes / dt);
1171 }
1172 }
1173
1174
1175
1176 static gboolean
1177 try_remove_file (file_op_context_t *ctx, const vfs_path_t *vpath, FileProgressStatus *status)
1178 {
1179 while (mc_unlink (vpath) != 0 && !ctx->ignore_all)
1180 {
1181 *status =
1182 file_error (ctx, TRUE, _ ("Cannot remove file \"%s\"\n%s"), vfs_path_as_str (vpath));
1183 if (*status == FILE_RETRY)
1184 continue;
1185 if (*status == FILE_IGNORE_ALL)
1186 ctx->ignore_all = TRUE;
1187 return FALSE;
1188 }
1189
1190 return TRUE;
1191 }
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207 static FileProgressStatus
1208 move_file_file (const WPanel *panel, file_op_context_t *ctx, const char *s, const char *d)
1209 {
1210 struct stat src_stat, dst_stat;
1211 FileProgressStatus return_status = FILE_CONT;
1212 gboolean copy_done = FALSE;
1213 gboolean old_ask_overwrite;
1214 vfs_path_t *src_vpath, *dst_vpath;
1215
1216 src_vpath = vfs_path_from_str (s);
1217 dst_vpath = vfs_path_from_str (d);
1218
1219 file_progress_show_source (ctx, src_vpath);
1220 file_progress_show_target (ctx, dst_vpath);
1221
1222
1223 if (file_progress_check_buttons (ctx) == FILE_ABORT)
1224 {
1225 return_status = FILE_ABORT;
1226 goto ret_fast;
1227 }
1228
1229 mc_refresh ();
1230
1231 while (mc_lstat (src_vpath, &src_stat) != 0)
1232 {
1233
1234 if (ctx->ignore_all)
1235 return_status = FILE_IGNORE_ALL;
1236 else
1237 {
1238 return_status = file_error (ctx, TRUE, _ ("Cannot stat file \"%s\"\n%s"), s);
1239 if (return_status == FILE_IGNORE_ALL)
1240 ctx->ignore_all = TRUE;
1241 }
1242
1243 if (return_status != FILE_RETRY)
1244 goto ret;
1245 }
1246
1247 if (mc_lstat (dst_vpath, &dst_stat) == 0)
1248 {
1249 if (check_same_file (ctx, s, &src_stat, d, &dst_stat, &return_status))
1250 goto ret;
1251
1252 if (S_ISDIR (dst_stat.st_mode))
1253 {
1254 message (D_ERROR, MSG_ERROR, _ ("Cannot overwrite directory \"%s\""), d);
1255 do_refresh ();
1256 return_status = FILE_SKIP;
1257 goto ret;
1258 }
1259
1260 if (confirm_overwrite)
1261 {
1262 return_status = query_replace (ctx, s, &src_stat, d, &dst_stat);
1263 if (return_status != FILE_CONT)
1264 goto ret;
1265 }
1266
1267 }
1268
1269 if (!ctx->do_append)
1270 {
1271 if (S_ISLNK (src_stat.st_mode) && ctx->stable_symlinks)
1272 {
1273 return_status = make_symlink (ctx, src_vpath, dst_vpath);
1274 if (return_status == FILE_CONT)
1275 {
1276 if (ctx->preserve)
1277 {
1278 mc_timesbuf_t times;
1279
1280 vfs_get_timesbuf_from_stat (&src_stat, ×);
1281 mc_utime (dst_vpath, ×);
1282 }
1283 goto retry_src_remove;
1284 }
1285 goto ret;
1286 }
1287
1288 if (mc_rename (src_vpath, dst_vpath) == 0)
1289 goto ret;
1290 }
1291 #if 0
1292
1293
1294
1295
1296 else
1297 errno = EXDEV;
1298
1299 if (errno != EXDEV)
1300 {
1301 if (ctx->ignore_all)
1302 return_status = FILE_IGNORE_ALL;
1303 else
1304 {
1305 return_status = files_error (ctx, _("Cannot move file \"%s\" to \"%s\"\n%s"), s, d);
1306 if (return_status == FILE_IGNORE_ALL)
1307 ctx->ignore_all = TRUE;
1308 if (return_status == FILE_RETRY)
1309 goto retry_rename;
1310 }
1311
1312 goto ret;
1313 }
1314 #endif
1315
1316
1317 if (panel != NULL)
1318 {
1319
1320
1321 return_status = panel_operate_init_totals (panel, src_vpath, &src_stat, ctx, TRUE,
1322 FILEGUI_DIALOG_ONE_ITEM);
1323 if (return_status != FILE_CONT)
1324 goto ret_fast;
1325 }
1326
1327 old_ask_overwrite = ctx->ask_overwrite;
1328 ctx->ask_overwrite = FALSE;
1329 return_status = copy_file_file (ctx, s, d);
1330 ctx->ask_overwrite = old_ask_overwrite;
1331 if (return_status != FILE_CONT)
1332 goto ret;
1333
1334 copy_done = TRUE;
1335
1336
1337
1338 if (panel == NULL)
1339 {
1340 file_progress_show_source (ctx, NULL);
1341 if (verbose)
1342 file_progress_show (ctx, 0, 0, "", FALSE);
1343
1344 return_status = file_progress_check_buttons (ctx);
1345 if (return_status != FILE_CONT)
1346 goto ret_fast;
1347 }
1348
1349 mc_refresh ();
1350
1351 retry_src_remove:
1352 if (!try_remove_file (ctx, src_vpath, &return_status) && panel == NULL)
1353 goto ret_fast;
1354
1355 ret:
1356 if (return_status != FILE_ABORT)
1357 {
1358
1359 if (!copy_done)
1360 progress_update_one (TRUE, ctx, src_stat.st_size);
1361 return_status = file_progress_check_buttons (ctx);
1362 }
1363 ret_fast:
1364 vfs_path_free (src_vpath, TRUE);
1365 vfs_path_free (dst_vpath, TRUE);
1366
1367 return return_status;
1368 }
1369
1370
1371
1372
1373
1374
1375
1376 static FileProgressStatus
1377 erase_file (file_op_context_t *ctx, const vfs_path_t *vpath)
1378 {
1379 struct stat buf;
1380 FileProgressStatus return_status;
1381
1382
1383 if (file_progress_show_deleting (ctx, vpath, &ctx->total_progress_count))
1384 {
1385 file_progress_show_count (ctx);
1386 if (file_progress_check_buttons (ctx) == FILE_ABORT)
1387 return FILE_ABORT;
1388
1389 mc_refresh ();
1390 }
1391
1392 if (ctx->total_progress_count != 0 && mc_lstat (vpath, &buf) != 0)
1393 {
1394
1395 buf.st_size = 0;
1396 }
1397
1398 if (!try_remove_file (ctx, vpath, &return_status) && return_status == FILE_ABORT)
1399 return FILE_ABORT;
1400
1401 if (ctx->total_progress_count == 0)
1402 return FILE_CONT;
1403
1404 return file_progress_check_buttons (ctx);
1405 }
1406
1407
1408
1409 static FileProgressStatus
1410 try_erase_dir (file_op_context_t *ctx, const vfs_path_t *vpath)
1411 {
1412 const char *dir;
1413 FileProgressStatus return_status = FILE_CONT;
1414
1415 dir = vfs_path_as_str (vpath);
1416
1417 while (my_rmdir (dir) != 0 && !ctx->ignore_all)
1418 {
1419 return_status = file_error (ctx, TRUE, _ ("Cannot remove directory \"%s\"\n%s"), dir);
1420 if (return_status == FILE_IGNORE_ALL)
1421 ctx->ignore_all = TRUE;
1422 if (return_status != FILE_RETRY)
1423 break;
1424 }
1425
1426 return return_status;
1427 }
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437 static FileProgressStatus
1438 recursive_erase (file_op_context_t *ctx, const vfs_path_t *vpath)
1439 {
1440 struct vfs_dirent *next;
1441 DIR *reading;
1442 FileProgressStatus return_status = FILE_CONT;
1443
1444 reading = mc_opendir (vpath);
1445 if (reading == NULL)
1446 return FILE_RETRY;
1447
1448 while ((next = mc_readdir (reading)) && return_status != FILE_ABORT)
1449 {
1450 vfs_path_t *tmp_vpath;
1451 struct stat buf;
1452
1453 if (DIR_IS_DOT (next->d_name) || DIR_IS_DOTDOT (next->d_name))
1454 continue;
1455
1456 tmp_vpath = vfs_path_append_new (vpath, next->d_name, (char *) NULL);
1457 if (mc_lstat (tmp_vpath, &buf) != 0)
1458 {
1459 mc_closedir (reading);
1460 vfs_path_free (tmp_vpath, TRUE);
1461 return FILE_RETRY;
1462 }
1463 if (S_ISDIR (buf.st_mode))
1464 return_status = recursive_erase (ctx, tmp_vpath);
1465 else
1466 return_status = erase_file (ctx, tmp_vpath);
1467 vfs_path_free (tmp_vpath, TRUE);
1468 }
1469 mc_closedir (reading);
1470
1471 if (return_status == FILE_ABORT)
1472 return FILE_ABORT;
1473
1474 file_progress_show_deleting (ctx, vpath, NULL);
1475 file_progress_show_count (ctx);
1476 if (file_progress_check_buttons (ctx) == FILE_ABORT)
1477 return FILE_ABORT;
1478
1479 mc_refresh ();
1480
1481 return try_erase_dir (ctx, vpath);
1482 }
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500 static int
1501 check_dir_is_empty (file_op_context_t *ctx, const vfs_path_t *vpath, FileProgressStatus *error)
1502 {
1503 DIR *dir;
1504 struct vfs_dirent *d;
1505 int i = 1;
1506
1507 while ((dir = mc_opendir (vpath)) == NULL)
1508 {
1509 if (ctx->ignore_all)
1510 *error = FILE_IGNORE_ALL;
1511 else
1512 {
1513 const FileProgressStatus status = file_error (
1514 ctx, TRUE, _ ("Cannot enter into directory \"%s\"\n%s"), vfs_path_as_str (vpath));
1515
1516 if (status == FILE_RETRY)
1517 continue;
1518 if (status == FILE_IGNORE_ALL)
1519 ctx->ignore_all = TRUE;
1520
1521 *error = status;
1522 }
1523
1524 return (-1);
1525 }
1526
1527 for (d = mc_readdir (dir); d != NULL; d = mc_readdir (dir))
1528 if (!DIR_IS_DOT (d->d_name) && !DIR_IS_DOTDOT (d->d_name))
1529 {
1530 i = 0;
1531 break;
1532 }
1533
1534 mc_closedir (dir);
1535 *error = FILE_CONT;
1536 return i;
1537 }
1538
1539
1540
1541 static FileProgressStatus
1542 erase_dir_iff_empty (file_op_context_t *ctx, const vfs_path_t *vpath)
1543 {
1544 FileProgressStatus error = FILE_CONT;
1545
1546 file_progress_show_deleting (ctx, vpath, NULL);
1547 file_progress_show_count (ctx);
1548 if (file_progress_check_buttons (ctx) == FILE_ABORT)
1549 return FILE_ABORT;
1550
1551 mc_refresh ();
1552
1553 const int res = check_dir_is_empty (ctx, vpath, &error);
1554
1555 if (res == -1)
1556 return error;
1557
1558 if (res != 1)
1559 return FILE_CONT;
1560
1561
1562 return try_erase_dir (ctx, vpath);
1563 }
1564
1565
1566
1567 static void
1568 erase_dir_after_copy (file_op_context_t *ctx, const vfs_path_t *vpath, FileProgressStatus *status)
1569 {
1570 if (ctx->erase_at_end && erase_list != NULL)
1571 {
1572
1573 ctx->total_progress_count = ctx->prev_total_progress_count;
1574
1575 while (!g_queue_is_empty (erase_list) && *status != FILE_ABORT)
1576 {
1577 link_t *lp;
1578
1579 lp = (link_t *) g_queue_pop_head (erase_list);
1580
1581 if (S_ISDIR (lp->st_mode))
1582 *status = erase_dir_iff_empty (ctx, lp->src_vpath);
1583 else
1584 *status = erase_file (ctx, lp->src_vpath);
1585
1586 free_link (lp);
1587 }
1588
1589
1590 ctx->prev_total_progress_count = ctx->total_progress_count;
1591 }
1592
1593 erase_dir_iff_empty (ctx, vpath);
1594 }
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610 static FileProgressStatus
1611 do_move_dir_dir (const WPanel *panel, file_op_context_t *ctx, const char *s, const char *d)
1612 {
1613 struct stat src_stat, dst_stat;
1614 FileProgressStatus return_status = FILE_CONT;
1615 gboolean move_over = FALSE;
1616 gboolean dstat_ok;
1617 vfs_path_t *src_vpath, *dst_vpath;
1618
1619 src_vpath = vfs_path_from_str (s);
1620 dst_vpath = vfs_path_from_str (d);
1621
1622 file_progress_show_source (ctx, src_vpath);
1623 file_progress_show_target (ctx, dst_vpath);
1624
1625
1626 if (panel != NULL && file_progress_check_buttons (ctx) == FILE_ABORT)
1627 {
1628 return_status = FILE_ABORT;
1629 goto ret_fast;
1630 }
1631
1632 mc_refresh ();
1633
1634 mc_stat (src_vpath, &src_stat);
1635
1636 dstat_ok = (mc_stat (dst_vpath, &dst_stat) == 0);
1637
1638 if (dstat_ok && check_same_file (ctx, s, &src_stat, d, &dst_stat, &return_status))
1639 goto ret_fast;
1640
1641 if (!dstat_ok)
1642 ;
1643 else if (!ctx->dive_into_subdirs)
1644 move_over = TRUE;
1645 else
1646 {
1647 vfs_path_t *tmp;
1648
1649 tmp = dst_vpath;
1650 dst_vpath = vfs_path_append_new (dst_vpath, x_basename (s), (char *) NULL);
1651 vfs_path_free (tmp, TRUE);
1652 }
1653
1654 d = vfs_path_as_str (dst_vpath);
1655
1656
1657 retry_dst_stat:
1658 if (mc_stat (dst_vpath, &dst_stat) == 0)
1659 {
1660 if (move_over)
1661 {
1662 if (panel != NULL)
1663 {
1664
1665
1666 return_status = panel_operate_init_totals (panel, src_vpath, &src_stat, ctx, TRUE,
1667 FILEGUI_DIALOG_MULTI_ITEM);
1668 if (return_status != FILE_CONT)
1669 goto ret;
1670 }
1671
1672 return_status = copy_dir_dir (ctx, s, d, FALSE, TRUE, TRUE, NULL);
1673
1674 if (return_status != FILE_CONT)
1675 goto ret;
1676 goto oktoret;
1677 }
1678 else if (ctx->ignore_all)
1679 return_status = FILE_IGNORE_ALL;
1680 else
1681 {
1682 if (S_ISDIR (dst_stat.st_mode))
1683 return_status =
1684 file_error (ctx, TRUE, _ ("Cannot overwrite directory \"%s\"\n%s"), d);
1685 else
1686 return_status = file_error (ctx, TRUE, _ ("Cannot overwrite file \"%s\"\n%s"), d);
1687 if (return_status == FILE_IGNORE_ALL)
1688 ctx->ignore_all = TRUE;
1689 if (return_status == FILE_RETRY)
1690 goto retry_dst_stat;
1691 }
1692
1693 goto ret_fast;
1694 }
1695
1696 retry_rename:
1697 if (mc_rename (src_vpath, dst_vpath) == 0)
1698 {
1699 return_status = FILE_CONT;
1700 goto ret;
1701 }
1702
1703 if (errno != EXDEV)
1704 {
1705 if (!ctx->ignore_all)
1706 {
1707 return_status =
1708 files_error (ctx, _ ("Cannot move directory \"%s\" to \"%s\"\n%s"), s, d);
1709 if (return_status == FILE_IGNORE_ALL)
1710 ctx->ignore_all = TRUE;
1711 if (return_status == FILE_RETRY)
1712 goto retry_rename;
1713 }
1714 goto ret;
1715 }
1716
1717
1718 if (panel != NULL)
1719 {
1720
1721
1722 return_status = panel_operate_init_totals (panel, src_vpath, &src_stat, ctx, TRUE,
1723 FILEGUI_DIALOG_MULTI_ITEM);
1724 if (return_status != FILE_CONT)
1725 goto ret;
1726 }
1727
1728 return_status = copy_dir_dir (ctx, s, d, FALSE, FALSE, TRUE, NULL);
1729
1730 if (return_status != FILE_CONT)
1731 goto ret;
1732
1733 oktoret:
1734
1735
1736 if (panel == NULL)
1737 {
1738 file_progress_show_source (ctx, NULL);
1739 file_progress_show_target (ctx, NULL);
1740 if (verbose)
1741 file_progress_show (ctx, 0, 0, "", FALSE);
1742
1743 return_status = file_progress_check_buttons (ctx);
1744 if (return_status != FILE_CONT)
1745 goto ret;
1746 }
1747
1748 mc_refresh ();
1749
1750 erase_dir_after_copy (ctx, src_vpath, &return_status);
1751
1752 ret:
1753 erase_list = free_erase_list (erase_list);
1754 ret_fast:
1755 vfs_path_free (src_vpath, TRUE);
1756 vfs_path_free (dst_vpath, TRUE);
1757 return return_status;
1758 }
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769 static const char *
1770 panel_get_file (const WPanel *panel)
1771 {
1772 const file_entry_t *fe;
1773
1774 if (get_current_type () == view_tree)
1775 {
1776 WTree *tree;
1777 const vfs_path_t *selected_name;
1778
1779 tree = (WTree *) get_panel_widget (get_current_index ());
1780 selected_name = tree_selected_name (tree);
1781 return vfs_path_as_str (selected_name);
1782 }
1783
1784 if (panel->marked != 0)
1785 {
1786 int i;
1787
1788 for (i = 0; i < panel->dir.len; i++)
1789 if (panel->dir.list[i].f.marked != 0)
1790 return panel->dir.list[i].fname->str;
1791 }
1792
1793 fe = panel_current_entry (panel);
1794
1795 return (fe == NULL ? NULL : fe->fname->str);
1796 }
1797
1798
1799
1800 static const char *
1801 check_single_entry (const WPanel *panel, gboolean force_single, struct stat *src_stat)
1802 {
1803 const char *source;
1804 gboolean ok;
1805
1806 if (force_single)
1807 {
1808 const file_entry_t *fe;
1809
1810 fe = panel_current_entry (panel);
1811 source = fe == NULL ? NULL : fe->fname->str;
1812 }
1813 else
1814 source = panel_get_file (panel);
1815
1816 if (source == NULL)
1817 return NULL;
1818
1819 ok = !DIR_IS_DOTDOT (source);
1820
1821 if (!ok)
1822 message (D_ERROR, MSG_ERROR, _ ("Cannot operate on \"..\"!"));
1823 else
1824 {
1825 vfs_path_t *source_vpath;
1826
1827 source_vpath = vfs_path_from_str (source);
1828
1829
1830 ok = mc_lstat (source_vpath, src_stat) == 0;
1831 if (!ok)
1832 {
1833 message (D_ERROR, MSG_ERROR, _ ("Cannot stat \"%s\"\n%s"), path_trunc (source, 30),
1834 unix_error_string (errno));
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 = ctx->preserve;
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 \"%s\"\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 =
2349 file_error (ctx, TRUE, _ ("Cannot stat source file \"%s\"\n%s"), src_path);
2350 if (return_status == FILE_IGNORE_ALL)
2351 ctx->ignore_all = TRUE;
2352 }
2353
2354 if (return_status != FILE_RETRY)
2355 {
2356
2357 progress_update_one (FALSE, ctx, 0);
2358 goto ret_fast;
2359 }
2360 }
2361
2362
2363 src_mode = src_stat.st_mode;
2364 src_uid = src_stat.st_uid;
2365 src_gid = src_stat.st_gid;
2366 file_size = src_stat.st_size;
2367
2368 while (attrs_ok && mc_fgetflags (src_vpath, &attrs) != 0)
2369 {
2370 attrs_ok = FALSE;
2371
2372
2373 if (attrs_ignore_error (errno))
2374 return_status = FILE_CONT;
2375 else if (ctx->ignore_all)
2376 return_status = FILE_IGNORE_ALL;
2377 else
2378 {
2379 return_status = file_error (
2380 ctx, TRUE, _ ("Cannot get ext2 attributes of source file \"%s\"\n%s"), src_path);
2381 if (return_status == FILE_IGNORE_ALL)
2382 ctx->ignore_all = TRUE;
2383 if (return_status == FILE_ABORT)
2384 goto ret_fast;
2385 }
2386
2387 if (return_status != FILE_RETRY)
2388 break;
2389
2390
2391 attrs_ok = TRUE;
2392 }
2393
2394 if (dst_exists)
2395 {
2396
2397 if (check_same_file (ctx, src_path, &src_stat, dst_path, &dst_stat, &return_status))
2398 goto ret_fast;
2399
2400
2401 if (ctx->ask_overwrite)
2402 {
2403 ctx->do_reget = 0;
2404 return_status = query_replace (ctx, src_path, &src_stat, dst_path, &dst_stat);
2405 if (return_status != FILE_CONT)
2406 goto ret_fast;
2407 }
2408 }
2409
2410 vfs_get_timesbuf_from_stat (&src_stat, ×);
2411
2412 if (!ctx->do_append)
2413 {
2414
2415 if (!ctx->follow_links)
2416 {
2417 switch (check_hardlinks (ctx, src_vpath, &src_stat, dst_vpath, &ctx->ignore_all))
2418 {
2419 case HARDLINK_OK:
2420
2421 return_status = FILE_CONT;
2422 goto ret_fast;
2423
2424 case HARDLINK_ABORT:
2425 return_status = FILE_ABORT;
2426 goto ret_fast;
2427
2428 default:
2429 break;
2430 }
2431 }
2432
2433 if (S_ISLNK (src_stat.st_mode))
2434 {
2435 return_status = make_symlink (ctx, src_vpath, dst_vpath);
2436 if (return_status == FILE_CONT && ctx->preserve)
2437 {
2438 mc_utime (dst_vpath, ×);
2439
2440 while (attrs_ok && mc_fsetflags (dst_vpath, attrs) != 0 && !ctx->ignore_all)
2441 {
2442 attrs_ok = FALSE;
2443
2444
2445 if (attrs_ignore_error (errno))
2446 return_status = FILE_CONT;
2447 else if (return_status == FILE_IGNORE_ALL)
2448 ctx->ignore_all = TRUE;
2449 else
2450 return_status = file_error (
2451 ctx, TRUE, _ ("Cannot set ext2 attributes of target file \"%s\"\n%s"),
2452 dst_path);
2453
2454 if (return_status != FILE_RETRY)
2455 break;
2456
2457
2458 attrs_ok = TRUE;
2459 }
2460 }
2461 goto ret_fast;
2462 }
2463
2464 if (S_ISCHR (src_stat.st_mode) || S_ISBLK (src_stat.st_mode) || S_ISFIFO (src_stat.st_mode)
2465 || S_ISNAM (src_stat.st_mode) || S_ISSOCK (src_stat.st_mode))
2466 {
2467 dev_t rdev = 0;
2468
2469 #ifdef HAVE_STRUCT_STAT_ST_RDEV
2470 rdev = src_stat.st_rdev;
2471 #endif
2472
2473 while (mc_mknod (dst_vpath, src_stat.st_mode & ctx->umask_kill, rdev) < 0
2474 && !ctx->ignore_all)
2475 {
2476 return_status =
2477 file_error (ctx, TRUE, _ ("Cannot create special file \"%s\"\n%s"), dst_path);
2478 if (return_status == FILE_RETRY)
2479 continue;
2480 if (return_status == FILE_IGNORE_ALL)
2481 ctx->ignore_all = TRUE;
2482 goto ret_fast;
2483 }
2484
2485
2486 while (ctx->preserve_uidgid
2487 && mc_chown (dst_vpath, src_stat.st_uid, src_stat.st_gid) != 0
2488 && !ctx->ignore_all)
2489 {
2490 temp_status =
2491 file_error (ctx, TRUE, _ ("Cannot chown target file \"%s\"\n%s"), dst_path);
2492 if (temp_status == FILE_IGNORE)
2493 break;
2494 if (temp_status == FILE_IGNORE_ALL)
2495 ctx->ignore_all = TRUE;
2496 if (temp_status != FILE_RETRY)
2497 {
2498 return_status = temp_status;
2499 goto ret_fast;
2500 }
2501 }
2502
2503 while (ctx->preserve && mc_chmod (dst_vpath, src_stat.st_mode & ctx->umask_kill) != 0
2504 && !ctx->ignore_all)
2505 {
2506 temp_status =
2507 file_error (ctx, TRUE, _ ("Cannot chmod target file \"%s\"\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 \"%s\"\n%s"),
2529 dst_path);
2530 if (temp_status == FILE_IGNORE)
2531 break;
2532 if (temp_status == FILE_IGNORE_ALL)
2533 ctx->ignore_all = TRUE;
2534 if (temp_status != FILE_RETRY)
2535 {
2536 return_status = temp_status;
2537 goto ret_fast;
2538 }
2539
2540
2541 attrs_ok = TRUE;
2542 }
2543
2544 return_status = FILE_CONT;
2545 mc_utime (dst_vpath, ×);
2546 goto ret_fast;
2547 }
2548 }
2549
2550 ctx->transfer_start = g_get_monotonic_time ();
2551
2552 while ((src_desc = mc_open (src_vpath, O_RDONLY | O_LINEAR)) < 0)
2553 {
2554 if (ctx->ignore_all)
2555 return_status = FILE_IGNORE_ALL;
2556 else
2557 {
2558 return_status =
2559 file_error (ctx, TRUE, _ ("Cannot open source file \"%s\"\n%s"), src_path);
2560 if (return_status == FILE_RETRY)
2561 continue;
2562 if (return_status == FILE_IGNORE_ALL)
2563 ctx->ignore_all = TRUE;
2564 ctx->do_append = FALSE;
2565 }
2566 goto ret;
2567 }
2568
2569 if (ctx->do_reget != 0 && mc_lseek (src_desc, ctx->do_reget, SEEK_SET) != ctx->do_reget)
2570 {
2571 message (D_ERROR, _ ("Warning"), _ ("Reget failed, about to overwrite file"));
2572 ctx->do_reget = 0;
2573 ctx->do_append = FALSE;
2574 }
2575
2576 while (mc_fstat (src_desc, &src_stat) != 0)
2577 {
2578 if (ctx->ignore_all)
2579 return_status = FILE_IGNORE_ALL;
2580 else
2581 {
2582 return_status =
2583 file_error (ctx, TRUE, _ ("Cannot fstat source file \"%s\"\n%s"), src_path);
2584 if (return_status == FILE_RETRY)
2585 continue;
2586 if (return_status == FILE_IGNORE_ALL)
2587 ctx->ignore_all = TRUE;
2588 ctx->do_append = FALSE;
2589 }
2590 goto ret;
2591 }
2592
2593
2594 src_mode = src_stat.st_mode;
2595 src_uid = src_stat.st_uid;
2596 src_gid = src_stat.st_gid;
2597 file_size = src_stat.st_size;
2598
2599 open_flags = O_WRONLY;
2600 if (!dst_exists)
2601 open_flags |= O_CREAT | O_EXCL;
2602 else if (ctx->do_append)
2603 open_flags |= O_APPEND;
2604 else
2605 open_flags |= O_CREAT | O_TRUNC;
2606
2607 while ((dest_desc = mc_open (dst_vpath, open_flags, src_mode)) < 0)
2608 {
2609 if (errno != EEXIST)
2610 {
2611 if (ctx->ignore_all)
2612 return_status = FILE_IGNORE_ALL;
2613 else
2614 {
2615 return_status =
2616 file_error (ctx, TRUE, _ ("Cannot create target file \"%s\"\n%s"), dst_path);
2617 if (return_status == FILE_RETRY)
2618 continue;
2619 if (return_status == FILE_IGNORE_ALL)
2620 ctx->ignore_all = TRUE;
2621 ctx->do_append = FALSE;
2622 }
2623 }
2624 goto ret;
2625 }
2626
2627
2628 dst_status = DEST_SHORT_QUERY;
2629
2630 appending = ctx->do_append;
2631 ctx->do_append = FALSE;
2632
2633
2634 if (vfs_clone_file (dest_desc, src_desc) == 0)
2635 {
2636 dst_status = DEST_FULL;
2637 return_status = FILE_CONT;
2638 goto ret;
2639 }
2640
2641
2642 while (mc_fstat (dest_desc, &dst_stat) != 0)
2643 {
2644 if (ctx->ignore_all)
2645 return_status = FILE_IGNORE_ALL;
2646 else
2647 {
2648 return_status =
2649 file_error (ctx, TRUE, _ ("Cannot fstat target file \"%s\"\n%s"), dst_path);
2650 if (return_status == FILE_RETRY)
2651 continue;
2652 if (return_status == FILE_IGNORE_ALL)
2653 ctx->ignore_all = TRUE;
2654 }
2655 goto ret;
2656 }
2657
2658
2659 while (mc_global.vfs.preallocate_space
2660 && vfs_preallocate (dest_desc, file_size, appending ? dst_stat.st_size : 0) != 0)
2661 {
2662 if (ctx->ignore_all)
2663 {
2664
2665 return_status = FILE_CONT;
2666 break;
2667 }
2668
2669 return_status = file_error (
2670 ctx, TRUE, _ ("Cannot preallocate space for target file \"%s\"\n%s"), dst_path);
2671
2672 if (return_status == FILE_IGNORE_ALL)
2673 ctx->ignore_all = TRUE;
2674
2675 if (ctx->ignore_all || return_status == FILE_IGNORE)
2676 {
2677
2678 return_status = FILE_CONT;
2679 break;
2680 }
2681
2682 if (return_status == FILE_ABORT)
2683 {
2684 mc_close (dest_desc);
2685 dest_desc = -1;
2686 mc_unlink (dst_vpath);
2687 dst_status = DEST_NONE;
2688 goto ret;
2689 }
2690
2691
2692 }
2693
2694 ctx->eta_secs = 0.0;
2695 ctx->bps = 0;
2696
2697 if (verbose)
2698 {
2699 if (ctx->total_bps == 0 || (file_size / ctx->total_bps) > FILEOP_UPDATE_INTERVAL)
2700 file_progress_show (ctx, 0, file_size, "", TRUE);
2701 else
2702 file_progress_show (ctx, 1, 1, "", TRUE);
2703 }
2704
2705 return_status = file_progress_check_buttons (ctx);
2706 mc_refresh ();
2707
2708 if (return_status == FILE_CONT)
2709 {
2710 off_t file_part = 0;
2711 gint64 tv_last_update = ctx->transfer_start;
2712 gint64 tv_last_input = 0;
2713 gboolean is_first_time = TRUE;
2714
2715 const size_t bufsize = io_blksize (dst_stat);
2716 buf = g_malloc (bufsize);
2717
2718 while (TRUE)
2719 {
2720 ssize_t n_read = -1;
2721
2722
2723 if (mc_ctl (src_desc, VFS_CTL_IS_NOTREADY, 0) == 0)
2724 while ((n_read = mc_read (src_desc, buf, bufsize)) < 0 && !ctx->ignore_all)
2725 {
2726 return_status =
2727 file_error (ctx, TRUE, _ ("Cannot read source file \"%s\"\n%s"), src_path);
2728 if (return_status == FILE_RETRY)
2729 continue;
2730 if (return_status == FILE_IGNORE_ALL)
2731 ctx->ignore_all = TRUE;
2732 goto ret;
2733 }
2734
2735 if (n_read == 0)
2736 break;
2737
2738 const gint64 tv_current = g_get_monotonic_time ();
2739
2740 if (n_read > 0)
2741 {
2742 ssize_t n_written;
2743 char *t = buf;
2744
2745 file_part += n_read;
2746
2747 tv_last_input = tv_current;
2748
2749
2750 while ((n_written = mc_write (dest_desc, t, (size_t) n_read)) < n_read)
2751 {
2752 gboolean write_errno_nospace;
2753
2754 if (n_written > 0)
2755 {
2756 n_read -= n_written;
2757 t += n_written;
2758 continue;
2759 }
2760
2761 write_errno_nospace = (n_written < 0 && errno == ENOSPC);
2762
2763 if (ctx->ignore_all)
2764 return_status = FILE_IGNORE_ALL;
2765 else
2766 return_status = file_error (
2767 ctx, TRUE, _ ("Cannot write target file \"%s\"\n%s"), dst_path);
2768
2769 if (return_status == FILE_IGNORE)
2770 {
2771 if (write_errno_nospace)
2772 goto ret;
2773 break;
2774 }
2775 if (return_status == FILE_IGNORE_ALL)
2776 {
2777 ctx->ignore_all = TRUE;
2778 if (write_errno_nospace)
2779 goto ret;
2780 }
2781 if (return_status != FILE_RETRY)
2782 goto ret;
2783 }
2784 }
2785
2786 ctx->progress_bytes = file_part + ctx->do_reget;
2787
2788 const gint64 usecs = tv_current - tv_last_update;
2789
2790 if (is_first_time || usecs > FILEOP_UPDATE_INTERVAL_US)
2791 {
2792 calc_copy_file_progress (ctx, tv_current, file_part, file_size - ctx->do_reget);
2793 tv_last_update = tv_current;
2794 }
2795
2796 is_first_time = FALSE;
2797
2798 if (verbose)
2799 {
2800 const gint64 total_usecs = tv_current - ctx->total_transfer_start;
2801 const gboolean force_update = total_usecs > FILEOP_UPDATE_INTERVAL_US;
2802
2803 const gint64 update_usecs = tv_current - tv_last_input;
2804 const char *stalled_msg =
2805 update_usecs > FILEOP_STALLING_INTERVAL_US ? _ ("(stalled)") : "";
2806
2807 file_progress_show (ctx, ctx->progress_bytes, file_size, stalled_msg, force_update);
2808 if (ctx->dialog_type == FILEGUI_DIALOG_MULTI_ITEM)
2809 {
2810 file_progress_show_count (ctx);
2811 file_progress_show_total (ctx, ctx->total_progress_bytes + ctx->progress_bytes,
2812 tv_current, force_update);
2813 }
2814
2815 mc_refresh ();
2816 }
2817
2818 return_status = file_progress_check_buttons (ctx);
2819 if (return_status != FILE_CONT)
2820 {
2821 int query_res;
2822
2823 const gint64 t1 = g_get_monotonic_time ();
2824 query_res =
2825 query_dialog (Q_ ("DialogTitle|Copy"), _ ("Incomplete file was retrieved"),
2826 D_ERROR, 3, _ ("&Delete"), _ ("&Keep"), _ ("&Continue copy"));
2827 const gint64 t2 = g_get_monotonic_time ();
2828 ctx->pauses += t2 - t1;
2829
2830
2831 calc_copy_file_progress (ctx, t2, file_part, file_size - ctx->do_reget);
2832
2833 switch (query_res)
2834 {
2835 case 0:
2836
2837 dst_status = DEST_SHORT_DELETE;
2838 goto ret;
2839
2840 case 1:
2841
2842 dst_status = DEST_SHORT_KEEP;
2843 goto ret;
2844
2845 default:
2846
2847 break;
2848 }
2849 }
2850 }
2851
2852
2853 dst_status = DEST_FULL;
2854 }
2855
2856 ret:
2857 g_free (buf);
2858
2859 rotate_dash (FALSE);
2860 while (src_desc != -1 && mc_close (src_desc) < 0 && !ctx->ignore_all)
2861 {
2862 temp_status = file_error (ctx, TRUE, _ ("Cannot close source file \"%s\"\n%s"), src_path);
2863 if (temp_status == FILE_RETRY)
2864 continue;
2865 if (temp_status == FILE_ABORT)
2866 return_status = temp_status;
2867 if (temp_status == FILE_IGNORE_ALL)
2868 ctx->ignore_all = TRUE;
2869 break;
2870 }
2871
2872 while (dest_desc != -1 && mc_close (dest_desc) < 0 && !ctx->ignore_all)
2873 {
2874 temp_status = file_error (ctx, TRUE, _ ("Cannot close target file \"%s\"\n%s"), dst_path);
2875 if (temp_status == FILE_RETRY)
2876 continue;
2877 if (temp_status == FILE_IGNORE_ALL)
2878 ctx->ignore_all = TRUE;
2879 return_status = temp_status;
2880 break;
2881 }
2882
2883 if (dst_status == DEST_SHORT_QUERY)
2884 {
2885
2886 if (query_dialog (Q_ ("DialogTitle|Copy"), _ ("Incomplete file was retrieved"), D_ERROR, 2,
2887 _ ("&Delete"), _ ("&Keep"))
2888 == 0)
2889 dst_status = DEST_SHORT_DELETE;
2890 else
2891 dst_status = DEST_SHORT_KEEP;
2892 }
2893
2894 if (dst_status == DEST_SHORT_DELETE)
2895 mc_unlink (dst_vpath);
2896 else if (dst_status == DEST_FULL && !appending)
2897 {
2898
2899
2900 while (ctx->preserve_uidgid && mc_chown (dst_vpath, src_uid, src_gid) != 0
2901 && !ctx->ignore_all)
2902 {
2903 temp_status =
2904 file_error (ctx, TRUE, _ ("Cannot chown target file \"%s\"\n%s"), dst_path);
2905 if (temp_status == FILE_ABORT)
2906 {
2907 return_status = FILE_ABORT;
2908 goto ret_fast;
2909 }
2910 if (temp_status == FILE_RETRY)
2911 continue;
2912 if (temp_status == FILE_IGNORE_ALL)
2913 {
2914 ctx->ignore_all = TRUE;
2915 return_status = FILE_CONT;
2916 }
2917 if (temp_status == FILE_IGNORE)
2918 return_status = FILE_CONT;
2919 break;
2920 }
2921
2922 while (ctx->preserve && mc_chmod (dst_vpath, (src_mode & ctx->umask_kill)) != 0
2923 && !ctx->ignore_all)
2924 {
2925 temp_status =
2926 file_error (ctx, TRUE, _ ("Cannot chmod target file \"%s\"\n%s"), dst_path);
2927 if (temp_status == FILE_ABORT)
2928 {
2929 return_status = FILE_ABORT;
2930 goto ret_fast;
2931 }
2932 if (temp_status == FILE_RETRY)
2933 continue;
2934 if (temp_status == FILE_IGNORE_ALL)
2935 {
2936 ctx->ignore_all = TRUE;
2937 return_status = FILE_CONT;
2938 }
2939 if (temp_status == FILE_IGNORE)
2940 return_status = FILE_CONT;
2941 break;
2942 }
2943
2944 if (!ctx->preserve && !dst_exists)
2945 {
2946 src_mode = umask (-1);
2947 umask (src_mode);
2948 src_mode = 0100666 & ~src_mode;
2949 mc_chmod (dst_vpath, (src_mode & ctx->umask_kill));
2950 }
2951 }
2952
2953 if (dst_status == DEST_FULL || dst_status == DEST_SHORT_KEEP)
2954 {
2955
2956 mc_utime (dst_vpath, ×);
2957
2958 while (attrs_ok && mc_fsetflags (dst_vpath, attrs) != 0 && !ctx->ignore_all)
2959 {
2960 attrs_ok = FALSE;
2961
2962
2963 if (attrs_ignore_error (errno))
2964 {
2965 return_status = FILE_CONT;
2966 break;
2967 }
2968
2969 temp_status = file_error (
2970 ctx, TRUE, _ ("Cannot set ext2 attributes for target file \"%s\"\n%s"), dst_path);
2971 if (temp_status == FILE_ABORT)
2972 return_status = FILE_ABORT;
2973 if (temp_status == FILE_RETRY)
2974 {
2975 attrs_ok = TRUE;
2976 continue;
2977 }
2978 if (temp_status == FILE_IGNORE_ALL)
2979 {
2980 ctx->ignore_all = TRUE;
2981 return_status = FILE_CONT;
2982 }
2983 if (temp_status == FILE_IGNORE)
2984 return_status = FILE_CONT;
2985 break;
2986 }
2987 }
2988
2989 progress_update_one (return_status == FILE_CONT, ctx, file_size);
2990 if (return_status == FILE_CONT)
2991 return_status = file_progress_check_buttons (ctx);
2992
2993 ret_fast:
2994 vfs_path_free (src_vpath, TRUE);
2995 vfs_path_free (dst_vpath, TRUE);
2996 return return_status;
2997 }
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007 FileProgressStatus
3008 copy_dir_dir (file_op_context_t *ctx, const char *s, const char *d, gboolean toplevel,
3009 gboolean move_over, gboolean do_delete, GSList *parent_dirs)
3010 {
3011 struct vfs_dirent *next;
3012 struct stat dst_stat, src_stat;
3013 unsigned long attrs = 0;
3014 gboolean attrs_ok = ctx->preserve;
3015 DIR *reading;
3016 FileProgressStatus return_status = FILE_CONT;
3017 link_t *lp;
3018 vfs_path_t *src_vpath, *dst_vpath;
3019 gboolean do_mkdir = TRUE;
3020
3021 src_vpath = vfs_path_from_str (s);
3022 dst_vpath = vfs_path_from_str (d);
3023
3024
3025
3026 retry_src_stat:
3027 while ((*ctx->stat_func) (src_vpath, &src_stat) != 0)
3028 {
3029 if (ctx->ignore_all)
3030 return_status = FILE_IGNORE_ALL;
3031 else
3032 {
3033 return_status =
3034 file_error (ctx, TRUE, _ ("Cannot stat source directory \"%s\"\n%s"), s);
3035 if (return_status == FILE_IGNORE_ALL)
3036 ctx->ignore_all = TRUE;
3037 }
3038
3039 if (return_status != FILE_RETRY)
3040 goto ret_fast;
3041 }
3042
3043 while (attrs_ok && mc_fgetflags (src_vpath, &attrs) != 0)
3044 {
3045 attrs_ok = FALSE;
3046
3047
3048 if (attrs_ignore_error (errno))
3049 return_status = FILE_CONT;
3050 else if (ctx->ignore_all)
3051 return_status = FILE_IGNORE_ALL;
3052 else
3053 {
3054 return_status = file_error (
3055 ctx, TRUE, _ ("Cannot get ext2 attributes of source directory \"%s\"\n%s"), s);
3056 if (return_status == FILE_IGNORE_ALL)
3057 ctx->ignore_all = TRUE;
3058 if (return_status == FILE_ABORT)
3059 goto ret_fast;
3060 }
3061
3062 if (return_status != FILE_RETRY)
3063 break;
3064
3065
3066 attrs_ok = TRUE;
3067 }
3068
3069 if (is_in_linklist (dest_dirs, src_vpath, &src_stat) != NULL)
3070 {
3071
3072
3073
3074 return_status = FILE_CONT;
3075 goto ret_fast;
3076 }
3077
3078
3079
3080
3081 if (ctx->preserve)
3082 {
3083 switch (check_hardlinks (ctx, src_vpath, &src_stat, dst_vpath, &ctx->ignore_all))
3084 {
3085 case HARDLINK_OK:
3086
3087 goto ret_fast;
3088
3089 case HARDLINK_ABORT:
3090 return_status = FILE_ABORT;
3091 goto ret_fast;
3092
3093 default:
3094 break;
3095 }
3096 }
3097
3098 if (!S_ISDIR (src_stat.st_mode))
3099 {
3100 if (ctx->ignore_all)
3101 return_status = FILE_IGNORE_ALL;
3102 else
3103 {
3104 return_status = file_error (ctx, TRUE, _ ("Source \"%s\" is not a directory\n%s"), s);
3105 if (return_status == FILE_RETRY)
3106 goto retry_src_stat;
3107 if (return_status == FILE_IGNORE_ALL)
3108 ctx->ignore_all = TRUE;
3109 }
3110 goto ret_fast;
3111 }
3112
3113 if (is_in_linklist (parent_dirs, src_vpath, &src_stat) != NULL)
3114 {
3115
3116 message (D_ERROR, MSG_ERROR, _ ("Cannot copy cyclic symbolic link\n\"%s\""), s);
3117 return_status = FILE_SKIP;
3118 goto ret_fast;
3119 }
3120
3121 lp = g_new0 (link_t, 1);
3122 lp->vfs = vfs_path_get_last_path_vfs (src_vpath);
3123 lp->ino = src_stat.st_ino;
3124 lp->dev = src_stat.st_dev;
3125 parent_dirs = g_slist_prepend (parent_dirs, lp);
3126
3127 retry_dst_stat:
3128
3129 if (mc_stat (dst_vpath, &dst_stat) != 0)
3130 {
3131
3132 if (move_over && mc_rename (src_vpath, dst_vpath) == 0)
3133 {
3134 return_status = FILE_CONT;
3135 goto ret;
3136 }
3137 }
3138 else
3139 {
3140
3141
3142
3143
3144
3145
3146
3147
3148 if (!S_ISDIR (dst_stat.st_mode))
3149 {
3150 if (ctx->ignore_all)
3151 return_status = FILE_IGNORE_ALL;
3152 else
3153 {
3154 return_status =
3155 file_error (ctx, TRUE, _ ("Destination \"%s\" must be a directory\n%s"), d);
3156 if (return_status == FILE_IGNORE_ALL)
3157 ctx->ignore_all = TRUE;
3158 if (return_status == FILE_RETRY)
3159 goto retry_dst_stat;
3160 }
3161 goto ret;
3162 }
3163
3164 if (toplevel && ctx->dive_into_subdirs)
3165 {
3166 vfs_path_t *tmp;
3167
3168 tmp = dst_vpath;
3169 dst_vpath = vfs_path_append_new (dst_vpath, x_basename (s), (char *) NULL);
3170 vfs_path_free (tmp, TRUE);
3171 }
3172 else
3173 do_mkdir = FALSE;
3174 }
3175
3176 d = vfs_path_as_str (dst_vpath);
3177
3178 if (do_mkdir)
3179 {
3180 while (my_mkdir (dst_vpath, (src_stat.st_mode & ctx->umask_kill) | S_IRWXU) != 0)
3181 {
3182 if (ctx->ignore_all)
3183 return_status = FILE_IGNORE_ALL;
3184 else
3185 {
3186 return_status =
3187 file_error (ctx, TRUE, _ ("Cannot create target directory \"%s\"\n%s"), d);
3188 if (return_status == FILE_IGNORE_ALL)
3189 ctx->ignore_all = TRUE;
3190 }
3191 if (return_status != FILE_RETRY)
3192 goto ret;
3193 }
3194
3195 lp = g_new0 (link_t, 1);
3196 mc_stat (dst_vpath, &dst_stat);
3197 lp->vfs = vfs_path_get_last_path_vfs (dst_vpath);
3198 lp->ino = dst_stat.st_ino;
3199 lp->dev = dst_stat.st_dev;
3200 dest_dirs = g_slist_prepend (dest_dirs, lp);
3201 }
3202
3203 if (ctx->preserve_uidgid)
3204 {
3205 while (mc_chown (dst_vpath, src_stat.st_uid, src_stat.st_gid) != 0)
3206 {
3207 if (ctx->ignore_all)
3208 return_status = FILE_IGNORE_ALL;
3209 else
3210 {
3211 return_status =
3212 file_error (ctx, TRUE, _ ("Cannot chown target directory \"%s\"\n%s"), d);
3213 if (return_status == FILE_IGNORE_ALL)
3214 ctx->ignore_all = TRUE;
3215 }
3216 if (return_status != FILE_RETRY)
3217 goto ret;
3218 }
3219 }
3220
3221
3222 reading = mc_opendir (src_vpath);
3223 if (reading == NULL)
3224 goto ret;
3225
3226 while ((next = mc_readdir (reading)) && return_status != FILE_ABORT)
3227 {
3228 char *path;
3229 vfs_path_t *tmp_vpath;
3230
3231
3232
3233
3234 if (DIR_IS_DOT (next->d_name) || DIR_IS_DOTDOT (next->d_name))
3235 continue;
3236
3237
3238 path = mc_build_filename (s, next->d_name, (char *) NULL);
3239 tmp_vpath = vfs_path_from_str (path);
3240
3241 (*ctx->stat_func) (tmp_vpath, &dst_stat);
3242 if (S_ISDIR (dst_stat.st_mode))
3243 {
3244 char *mdpath;
3245
3246 mdpath = mc_build_filename (d, next->d_name, (char *) NULL);
3247
3248
3249
3250
3251
3252
3253 return_status = copy_dir_dir (ctx, path, mdpath, FALSE, FALSE, do_delete, parent_dirs);
3254 g_free (mdpath);
3255 }
3256 else
3257 {
3258 char *dest_file;
3259
3260 dest_file = mc_build_filename (d, x_basename (path), (char *) NULL);
3261 return_status = copy_file_file (ctx, path, dest_file);
3262 g_free (dest_file);
3263 }
3264
3265 g_free (path);
3266
3267 if (do_delete && return_status == FILE_CONT)
3268 {
3269 if (ctx->erase_at_end)
3270 {
3271 if (erase_list == NULL)
3272 erase_list = g_queue_new ();
3273
3274 lp = g_new0 (link_t, 1);
3275 lp->src_vpath = tmp_vpath;
3276 lp->st_mode = dst_stat.st_mode;
3277 g_queue_push_tail (erase_list, lp);
3278 tmp_vpath = NULL;
3279 }
3280 else if (S_ISDIR (dst_stat.st_mode))
3281 return_status = erase_dir_iff_empty (ctx, tmp_vpath);
3282 else
3283 return_status = erase_file (ctx, tmp_vpath);
3284 }
3285 vfs_path_free (tmp_vpath, TRUE);
3286 }
3287 mc_closedir (reading);
3288
3289 if (ctx->preserve)
3290 {
3291 mc_timesbuf_t times;
3292
3293 mc_chmod (dst_vpath, src_stat.st_mode & ctx->umask_kill);
3294
3295 if (attrs_ok)
3296 mc_fsetflags (dst_vpath, attrs);
3297
3298 vfs_get_timesbuf_from_stat (&src_stat, ×);
3299 mc_utime (dst_vpath, ×);
3300 }
3301 else
3302 {
3303 src_stat.st_mode = umask (-1);
3304 umask (src_stat.st_mode);
3305 src_stat.st_mode = 0100777 & ~src_stat.st_mode;
3306 mc_chmod (dst_vpath, src_stat.st_mode & ctx->umask_kill);
3307 }
3308
3309 ret:
3310 free_link (parent_dirs->data);
3311 g_slist_free_1 (parent_dirs);
3312 ret_fast:
3313 vfs_path_free (src_vpath, TRUE);
3314 vfs_path_free (dst_vpath, TRUE);
3315 return return_status;
3316 }
3317
3318
3319
3320
3321
3322
3323 FileProgressStatus
3324 move_dir_dir (file_op_context_t *ctx, const char *s, const char *d)
3325 {
3326 return do_move_dir_dir (NULL, ctx, s, d);
3327 }
3328
3329
3330
3331
3332
3333
3334 FileProgressStatus
3335 erase_dir (file_op_context_t *ctx, const vfs_path_t *vpath)
3336 {
3337 FileProgressStatus error = FILE_CONT;
3338
3339 file_progress_show_deleting (ctx, vpath, NULL);
3340 file_progress_show_count (ctx);
3341 if (file_progress_check_buttons (ctx) == FILE_ABORT)
3342 return FILE_ABORT;
3343
3344 mc_refresh ();
3345
3346
3347
3348
3349
3350
3351
3352
3353 const int res = check_dir_is_empty (ctx, vpath, &error);
3354
3355 if (res == -1)
3356 return error;
3357
3358 if (res == 0)
3359 {
3360
3361 error = query_recursive (ctx, vfs_path_as_str (vpath));
3362 if (error == FILE_CONT)
3363 error = recursive_erase (ctx, vpath);
3364 return error;
3365 }
3366
3367 return try_erase_dir (ctx, vpath);
3368 }
3369
3370
3371
3372
3373
3374
3375 void
3376 dirsize_status_init_cb (status_msg_t *sm)
3377 {
3378 dirsize_status_msg_t *dsm = (dirsize_status_msg_t *) sm;
3379 WGroup *gd = GROUP (sm->dlg);
3380 Widget *wd = WIDGET (sm->dlg);
3381 WRect r = wd->rect;
3382
3383 const char *b1_name = N_ ("&Abort");
3384 const char *b2_name = N_ ("&Skip");
3385 int b_width, ui_width;
3386
3387 #ifdef ENABLE_NLS
3388 b1_name = _ (b1_name);
3389 b2_name = _ (b2_name);
3390 #endif
3391
3392 b_width = str_term_width1 (b1_name) + 4;
3393 if (dsm->allow_skip)
3394 b_width += str_term_width1 (b2_name) + 4 + 1;
3395
3396 ui_width = MAX (COLS / 2, b_width + 6);
3397 dsm->dirname = label_new (2, 3, NULL);
3398 group_add_widget (gd, dsm->dirname);
3399 dsm->count_size = label_new (3, 3, NULL);
3400 group_add_widget (gd, dsm->count_size);
3401 group_add_widget (gd, hline_new (4, -1, -1));
3402
3403 dsm->abort_button = WIDGET (button_new (5, 3, FILE_ABORT, NORMAL_BUTTON, b1_name, NULL));
3404 group_add_widget (gd, dsm->abort_button);
3405 if (dsm->allow_skip)
3406 {
3407 dsm->skip_button = WIDGET (button_new (5, 3, FILE_SKIP, NORMAL_BUTTON, b2_name, NULL));
3408 group_add_widget (gd, dsm->skip_button);
3409 widget_select (dsm->skip_button);
3410 }
3411
3412 r.lines = 8;
3413 r.cols = ui_width;
3414 widget_set_size_rect (wd, &r);
3415 dirsize_status_locate_buttons (dsm);
3416 }
3417
3418
3419
3420 int
3421 dirsize_status_update_cb (status_msg_t *sm)
3422 {
3423 dirsize_status_msg_t *dsm = (dirsize_status_msg_t *) sm;
3424 Widget *wd = WIDGET (sm->dlg);
3425 WRect r = wd->rect;
3426
3427
3428 label_set_textv (dsm->count_size, _ ("Directories: %zu, total size: %s"), dsm->dir_count,
3429 size_trunc_sep (dsm->total_size, panels_options.kilobyte_si));
3430
3431
3432 if (WIDGET (dsm->count_size)->rect.cols + 6 > r.cols)
3433 {
3434 r.cols = WIDGET (dsm->count_size)->rect.cols + 6;
3435 widget_set_size_rect (wd, &r);
3436 dirsize_status_locate_buttons (dsm);
3437 widget_draw (wd);
3438
3439 }
3440
3441
3442 label_set_text (dsm->dirname,
3443 str_trunc (vfs_path_as_str (dsm->dirname_vpath), wd->rect.cols - 6));
3444
3445 switch (status_msg_common_update (sm))
3446 {
3447 case B_CANCEL:
3448 case FILE_ABORT:
3449 return FILE_ABORT;
3450 case FILE_SKIP:
3451 return FILE_SKIP;
3452 default:
3453 return FILE_CONT;
3454 }
3455 }
3456
3457
3458
3459 void
3460 dirsize_status_deinit_cb (status_msg_t *sm)
3461 {
3462 (void) sm;
3463
3464
3465 if (get_other_type () == view_listing)
3466 other_panel->dirty = TRUE;
3467 }
3468
3469
3470
3471
3472
3473
3474
3475
3476 FileProgressStatus
3477 compute_dir_size (const vfs_path_t *dirname_vpath, dirsize_status_msg_t *sm, size_t *ret_dir_count,
3478 size_t *ret_marked_count, uintmax_t *ret_total, gboolean follow_symlinks)
3479 {
3480 return do_compute_dir_size (dirname_vpath, sm, ret_dir_count, ret_marked_count, ret_total,
3481 follow_symlinks ? mc_stat : mc_lstat);
3482 }
3483
3484
3485
3486
3487
3488
3489
3490
3491
3492
3493
3494
3495
3496
3497
3498 gboolean
3499 panel_operate (void *source_panel, FileOperation operation, gboolean force_single)
3500 {
3501 WPanel *panel = PANEL (source_panel);
3502 const gboolean single_entry =
3503 force_single || (panel->marked <= 1) || (get_current_type () == view_tree);
3504
3505 const char *source = NULL;
3506 char *dest = NULL;
3507 vfs_path_t *dest_vpath = NULL;
3508 vfs_path_t *save_cwd = NULL, *save_dest = NULL;
3509 struct stat src_stat;
3510 gboolean ret_val = TRUE;
3511 int i;
3512 FileProgressStatus value;
3513 file_op_context_t *ctx;
3514 filegui_dialog_type_t dialog_type = FILEGUI_DIALOG_ONE_ITEM;
3515
3516 gboolean do_bg = FALSE;
3517
3518 static gboolean i18n_flag = FALSE;
3519 if (!i18n_flag)
3520 {
3521 for (i = G_N_ELEMENTS (op_names); i-- != 0;)
3522 op_names[i] = Q_ (op_names[i]);
3523 i18n_flag = TRUE;
3524 }
3525
3526 linklist = free_linklist (linklist);
3527 dest_dirs = free_linklist (dest_dirs);
3528
3529 save_cwds_stat ();
3530
3531 if (single_entry)
3532 {
3533 source = check_single_entry (panel, force_single, &src_stat);
3534
3535 if (source == NULL)
3536 return FALSE;
3537 }
3538
3539 ctx = file_op_context_new (operation);
3540
3541
3542 if (operation != OP_DELETE)
3543 {
3544 dest = do_confirm_copy_move (panel, force_single, source, &src_stat, ctx, &do_bg);
3545 if (dest == NULL)
3546 {
3547 ret_val = FALSE;
3548 goto ret_fast;
3549 }
3550
3551 dest_vpath = vfs_path_from_str (dest);
3552 }
3553 else if (confirm_delete && !do_confirm_erase (panel, source, &src_stat))
3554 {
3555 ret_val = FALSE;
3556 goto ret_fast;
3557 }
3558
3559 ctx->total_transfer_start = g_get_monotonic_time ();
3560
3561 #ifdef ENABLE_BACKGROUND
3562
3563 if (do_bg)
3564 {
3565 int v;
3566
3567 v = do_background (ctx,
3568 g_strconcat (op_names[operation], ": ",
3569 vfs_path_as_str (panel->cwd_vpath), (char *) NULL));
3570 if (v == -1)
3571 message (D_ERROR, MSG_ERROR, _ ("Sorry, I could not put the job in background"));
3572
3573
3574 if (v == 1)
3575 {
3576 mc_setctl (panel->cwd_vpath, VFS_SETCTL_FORGET, NULL);
3577
3578 mc_setctl (dest_vpath, VFS_SETCTL_FORGET, NULL);
3579 vfs_path_free (dest_vpath, TRUE);
3580 g_free (dest);
3581
3582 return FALSE;
3583 }
3584 }
3585 else
3586 #endif
3587 {
3588 const file_entry_t *fe;
3589
3590 if (operation == OP_DELETE)
3591 dialog_type = FILEGUI_DIALOG_DELETE_ITEM;
3592 else if (single_entry
3593 && ((fe = panel_current_entry (panel)) == NULL ? FALSE : S_ISDIR (fe->st.st_mode)))
3594 dialog_type = FILEGUI_DIALOG_MULTI_ITEM;
3595 else if (single_entry || force_single)
3596 dialog_type = FILEGUI_DIALOG_ONE_ITEM;
3597 else
3598 dialog_type = FILEGUI_DIALOG_MULTI_ITEM;
3599 }
3600
3601
3602
3603
3604
3605 if ((dest != NULL)
3606 && (mc_setctl (dest_vpath, VFS_SETCTL_STALE_DATA, GUINT_TO_POINTER (1)) != 0))
3607 save_dest = vfs_path_from_str (dest);
3608
3609 if ((vfs_path_tokens_count (panel->cwd_vpath) != 0)
3610 && (mc_setctl (panel->cwd_vpath, VFS_SETCTL_STALE_DATA, GUINT_TO_POINTER (1)) != 0))
3611 save_cwd = vfs_path_clone (panel->cwd_vpath);
3612
3613
3614
3615
3616 if (single_entry)
3617 {
3618
3619
3620
3621 if ((operation != OP_COPY) && (get_current_type () == view_tree))
3622 {
3623 vfs_path_t *vpath;
3624 int chdir_retcode;
3625
3626 vpath = vfs_path_from_str (PATH_SEP_STR);
3627 chdir_retcode = mc_chdir (vpath);
3628 vfs_path_free (vpath, TRUE);
3629 if (chdir_retcode < 0)
3630 {
3631 ret_val = FALSE;
3632 goto clean_up;
3633 }
3634 }
3635
3636 value = operate_single_file (panel, ctx, source, &src_stat, dest, dialog_type);
3637 if ((value == FILE_CONT) && !force_single)
3638 unmark_files (panel);
3639 }
3640 else
3641 {
3642
3643
3644
3645 while (operation != OP_DELETE)
3646 {
3647 int dst_result;
3648 struct stat dst_stat;
3649
3650 dst_result = mc_stat (dest_vpath, &dst_stat);
3651
3652 if ((dst_result != 0) || S_ISDIR (dst_stat.st_mode))
3653 break;
3654
3655 if (ctx->ignore_all
3656 || file_error (ctx, TRUE, _ ("Destination \"%s\" must be a directory\n%s"), dest)
3657 != FILE_RETRY)
3658 goto clean_up;
3659 }
3660
3661
3662
3663
3664
3665
3666 value =
3667 panel_operate_init_totals (panel, NULL, NULL, ctx, file_op_compute_totals, dialog_type);
3668 if (value == FILE_CONT)
3669
3670 for (i = 0; i < panel->dir.len; i++)
3671 {
3672 const char *source2;
3673
3674 if (panel->dir.list[i].f.marked == 0)
3675 continue;
3676
3677 source2 = panel->dir.list[i].fname->str;
3678 src_stat = panel->dir.list[i].st;
3679
3680 value = operate_one_file (panel, ctx, source2, &src_stat, dest);
3681 if (value == FILE_ABORT)
3682 break;
3683
3684 if (value == FILE_CONT)
3685 do_file_mark (panel, i, 0);
3686
3687 mc_refresh ();
3688 }
3689 }
3690
3691 clean_up:
3692
3693 if (save_cwd != NULL)
3694 {
3695 mc_setctl (save_cwd, VFS_SETCTL_STALE_DATA, NULL);
3696 vfs_path_free (save_cwd, TRUE);
3697 }
3698
3699 if (save_dest != NULL)
3700 {
3701 mc_setctl (save_dest, VFS_SETCTL_STALE_DATA, NULL);
3702 vfs_path_free (save_dest, TRUE);
3703 }
3704
3705 linklist = free_linklist (linklist);
3706 dest_dirs = free_linklist (dest_dirs);
3707 g_free (dest);
3708 vfs_path_free (dest_vpath, TRUE);
3709 MC_PTR_FREE (ctx->dest_mask);
3710
3711 #ifdef ENABLE_BACKGROUND
3712
3713 if (mc_global.we_are_background)
3714 {
3715
3716
3717 ctx->pid = getpid ();
3718 parent_call ((void *) end_bg_process, ctx, 0);
3719
3720 vfs_shut ();
3721 my_exit (EXIT_SUCCESS);
3722 }
3723 #endif
3724
3725 ret_fast:
3726 file_op_context_destroy (ctx);
3727
3728 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
3729 repaint_screen ();
3730
3731 return ret_val;
3732 }
3733
3734
3735
3736
3737
3738
3739 FileProgressStatus
3740 file_error (file_op_context_t *ctx, gboolean allow_retry, const char *format, const char *file)
3741 {
3742 char buf[BUF_MEDIUM];
3743
3744 g_snprintf (buf, sizeof (buf), format, path_trunc (file, 30), unix_error_string (errno));
3745
3746 return do_file_error (ctx, allow_retry, buf);
3747 }
3748
3749
3750
3751
3752
3753
3754
3755