2 Copyright (C) 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003,
3 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
5 Written by: 1994, 1995 Janne Kukonlehto
6 1994, 1995 Fred Leeflang
7 1994, 1995, 1996 Miguel de Icaza
8 1995, 1996 Jakub Jelinek
12 The copy code was based in GNU's cp, and was written by:
13 Torbjorn Granlund, David MacKenzie, and Jim Meyering.
15 The move code was based in GNU's mv, and was written by:
16 Mike Parker and David MacKenzie.
18 Janne Kukonlehto added much error recovery to them for being used
19 in an interactive program.
21 This program is free software; you can redistribute it and/or modify
22 it under the terms of the GNU General Public License as published by
23 the Free Software Foundation; either version 2 of the License, or
24 (at your option) any later version.
26 This program is distributed in the hope that it will be useful,
27 but WITHOUT ANY WARRANTY; without even the implied warranty of
28 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 GNU General Public License for more details.
31 You should have received a copy of the GNU General Public License
32 along with this program; if not, write to the Free Software
33 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
36 * Please note that all dialogs used here must be safe for background
41 * \brief Source: file management
44 /* {{{ Include files */
53 #include <sys/types.h>
61 #include "../src/tty/tty.h"
62 #include "../src/tty/key.h"
64 #include "../src/search/search.h"
73 #include "background.h" /* we_are_background */
74 #include "../src/strescape.h"
77 /* Needed for current_panel, other_panel and WTree */
83 #include "../vfs/vfs-impl.h"
87 /* Hack: the vfs code should not rely on this */
88 #define WITH_FULL_PATHS 1
93 * Whether the Midnight Commander tries to provide more
94 * information about copy/move sizes and bytes transfered
95 * at the expense of some speed
97 int file_op_compute_totals
= 1;
99 /* This is a hard link cache */
102 struct vfs_class
*vfs
;
110 /* the hard link cache */
111 static struct link
*linklist
= NULL
;
113 /* the files-to-be-erased list */
114 static struct link
*erase_list
;
117 * In copy_dir_dir we use two additional single linked lists: The first -
118 * variable name `parent_dirs' - holds information about already copied
119 * directories and is used to detect cyclic symbolic links.
120 * The second (`dest_dirs' below) holds information about just created
121 * target directories and is used to detect when an directory is copied
122 * into itself (we don't want to copy infinitly).
123 * Both lists don't use the linkcount and name structure members of struct
126 static struct link
*dest_dirs
= NULL
;
128 /* TRANSLATORS: no need to translate 'DialogTitle', it's just a context prefix */
129 const char *op_names
[3] = {
130 N_("DialogTitle|Copy"),
131 N_("DialogTitle|Move"),
132 N_("DialogTitle|Delete")
137 static FileProgressStatus
query_replace (FileOpContext
* ctx
, const char *destname
,
138 struct stat
*_s_stat
, struct stat
*_d_stat
);
139 static FileProgressStatus
query_recursive (FileOpContext
* ctx
, const char *s
);
140 static FileProgressStatus
do_file_error (const char *str
);
141 static FileProgressStatus
erase_dir_iff_empty (FileOpContext
*ctx
, const char *s
);
142 static FileProgressStatus
erase_file (FileOpContext
*ctx
, const char *s
,
143 off_t
*progress_count
, double *progress_bytes
,
144 int is_toplevel_file
);
145 static FileProgressStatus
files_error (const char *format
, const char *file1
,
148 static FileProgressStatus transform_error
= FILE_CONT
;
151 transform_source (FileOpContext
*ctx
, const char *source
)
156 s
= g_strdup (source
);
158 /* We remove \n from the filename since regex routines would use \n as an anchor */
159 /* this is just to be allowed to maniupulate file names with \n on it */
160 for (q
= s
; *q
!= '\0'; q
++)
164 fnsource
= (char *) x_basename (s
);
166 if (mc_search_run (ctx
->search_handle
, fnsource
, 0, strlen (fnsource
), NULL
))
167 q
= mc_search_prepare_replace_str2 (ctx
->search_handle
, ctx
->dest_mask
);
170 transform_error
= FILE_SKIP
;
178 free_linklist (struct link
**lc_linklist
)
180 struct link
*lp
, *lp2
;
182 for (lp
= *lc_linklist
; lp
!= NULL
; lp
= lp2
) {
190 is_in_linklist (struct link
*lp
, const char *path
, struct stat
*sb
)
192 ino_t ino
= sb
->st_ino
;
193 dev_t dev
= sb
->st_dev
;
195 struct vfs_class
*vfs
= vfs_get_class (path
);
202 if (lp
->ino
== ino
&& lp
->dev
== dev
)
210 * Returns 0 if the inode wasn't found in the cache and 1 if it was found
211 * and a hardlink was succesfully made
214 check_hardlinks (const char *src_name
, const char *dst_name
, struct stat
*pstat
)
217 struct vfs_class
*my_vfs
= vfs_get_class (src_name
);
218 ino_t ino
= pstat
->st_ino
;
219 dev_t dev
= pstat
->st_dev
;
220 struct stat link_stat
;
223 if (vfs_file_class_flags (src_name
) & VFSF_NOLINKS
)
226 for (lp
= linklist
; lp
!= NULL
; lp
= lp
->next
)
227 if (lp
->vfs
== my_vfs
&& lp
->ino
== ino
&& lp
->dev
== dev
) {
228 if (!mc_stat (lp
->name
, &link_stat
) && link_stat
.st_ino
== ino
229 && link_stat
.st_dev
== dev
230 && vfs_get_class (lp
->name
) == my_vfs
) {
231 p
= strchr (lp
->name
, 0) + 1; /* i.e. where the `name' file
233 if (vfs_get_class (dst_name
) == vfs_get_class (p
)) {
234 if (!mc_stat (p
, &link_stat
)) {
235 if (!mc_link (p
, dst_name
))
240 message (D_ERROR
, MSG_ERROR
, _(" Cannot make the hardlink "));
243 lp
= (struct link
*) g_malloc (sizeof (struct link
) + strlen (src_name
)
244 + strlen (dst_name
) + 1);
250 strcpy (lp
->name
, src_name
);
251 lpdstname
= lp
->name
+ strlen(lp
->name
) + 1;
252 strcpy (lpdstname
, dst_name
);
260 * Duplicate the contents of the symbolic link src_path in dst_path.
261 * Try to make a stable symlink if the option "stable symlink" was
262 * set in the file mask dialog.
263 * If dst_path is an existing symlink it will be deleted silently
264 * (upper levels take already care of existing files at dst_path).
266 static FileProgressStatus
267 make_symlink (FileOpContext
*ctx
, const char *src_path
, const char *dst_path
)
269 char link_target
[MC_MAXPATHLEN
];
271 FileProgressStatus return_status
;
275 if (mc_lstat (dst_path
, &sb
) == 0 && S_ISLNK (sb
.st_mode
))
281 len
= mc_readlink (src_path
, link_target
, MC_MAXPATHLEN
- 1);
284 file_error (_(" Cannot read source link \"%s\" \n %s "),
286 if (return_status
== FILE_RETRY
)
287 goto retry_src_readlink
;
288 return return_status
;
290 link_target
[len
] = 0;
292 if (ctx
->stable_symlinks
)
293 if (!vfs_file_is_local (src_path
) || !vfs_file_is_local (dst_path
)) {
294 message (D_ERROR
, MSG_ERROR
,
295 _(" Cannot make stable symlinks across "
296 "non-local filesystems: \n\n"
297 " Option Stable Symlinks will be disabled "));
298 ctx
->stable_symlinks
= 0;
301 if (ctx
->stable_symlinks
&& *link_target
!= PATH_SEP
) {
304 const char *r
= strrchr (src_path
, PATH_SEP
);
307 p
= g_strndup (src_path
, r
- src_path
+ 1);
308 if (*dst_path
== PATH_SEP
)
309 q
= g_strdup (dst_path
);
311 q
= g_strconcat (p
, dst_path
, (char *) NULL
);
312 s
= strrchr (q
, PATH_SEP
);
315 s
= g_strconcat (p
, link_target
, (char *) NULL
);
317 g_strlcpy (link_target
, s
, sizeof (link_target
));
319 s
= diff_two_paths (q
, link_target
);
321 g_strlcpy (link_target
, s
, sizeof (link_target
));
330 if (mc_symlink (link_target
, dst_path
) == 0)
334 * if dst_exists, it is obvious that this had failed.
335 * We can delete the old symlink and try again...
337 if (dst_is_symlink
) {
338 if (!mc_unlink (dst_path
))
339 if (mc_symlink (link_target
, dst_path
) == 0)
344 file_error (_(" Cannot create target symlink \"%s\" \n %s "),
346 if (return_status
== FILE_RETRY
)
347 goto retry_dst_symlink
;
348 return return_status
;
352 progress_update_one (FileOpContext
*ctx
,
353 off_t
*progress_count
,
354 double *progress_bytes
, off_t add
, int is_toplevel_file
)
358 if (is_toplevel_file
|| ctx
->progress_totals_computed
) {
360 (*progress_bytes
) += add
;
363 /* Apply some heuristic here to not call the update stuff very often */
365 file_progress_show_count (ctx
, *progress_count
,
366 ctx
->progress_count
);
367 if (ret
!= FILE_CONT
)
370 file_progress_show_bytes (ctx
, *progress_bytes
,
371 ctx
->progress_bytes
);
376 /* Status of the destination file */
378 DEST_NONE
, /* Not created */
379 DEST_SHORT
, /* Created, not fully copied */
380 DEST_FULL
/* Created, fully copied */
383 static FileProgressStatus
384 real_warn_same_file (enum OperationMode mode
, const char *fmt
,
385 const char *a
, const char *b
)
389 const char *head_msg
;
391 head_msg
= mode
== Foreground
? MSG_ERROR
:
392 _(" Background process error ");
394 msg
= g_strdup_printf (fmt
, a
, b
);
395 result
= query_dialog (head_msg
, msg
, D_ERROR
, 2, _("&Skip"), _("&Abort"));
398 if ( result
) { /* 1 == Abort */
405 #ifdef WITH_BACKGROUND
406 static FileProgressStatus
407 warn_same_file (const char *fmt
, const char *a
, const char *b
)
411 FileProgressStatus (*f
) (enum OperationMode
, const char *fmt
,
412 const char *a
, const char *b
);
414 pntr
.f
= real_warn_same_file
;
416 if (we_are_background
)
417 return parent_call (pntr
.p
, NULL
, 3, strlen (fmt
),
418 fmt
, strlen(a
), a
, strlen(b
), b
);
420 return real_warn_same_file (Foreground
, fmt
, a
, b
);
423 static FileProgressStatus
424 warn_same_file (const char *fmt
, const char *a
, const char *b
)
426 return real_warn_same_file (Foreground
, fmt
, a
, b
);
431 copy_file_file (FileOpContext
*ctx
, const char *src_path
, const char *dst_path
,
432 int ask_overwrite
, off_t
*progress_count
,
433 double *progress_bytes
, int is_toplevel_file
)
435 uid_t src_uid
= (uid_t
) - 1;
436 gid_t src_gid
= (gid_t
) - 1;
439 int buf_size
= BUF_8K
;
440 int src_desc
, dest_desc
= -1;
441 int n_read
, n_written
;
442 mode_t src_mode
= 0; /* The mode of the source file */
445 int dst_exists
= 0, appending
= 0;
446 off_t n_read_total
= 0, file_size
= -1;
447 FileProgressStatus return_status
, temp_status
;
448 struct timeval tv_transfer_start
;
449 int dst_status
= DEST_NONE
; /* 1 if the file is not fully copied */
452 /* FIXME: We should not be using global variables! */
454 return_status
= FILE_RETRY
;
456 if (file_progress_show_source (ctx
, src_path
) == FILE_ABORT
||
457 file_progress_show_target (ctx
, dst_path
) == FILE_ABORT
)
462 while (mc_stat (dst_path
, &sb2
) == 0) {
463 if (S_ISDIR (sb2
.st_mode
)) {
465 file_error (_(" Cannot overwrite directory \"%s\" \n %s "),
467 if (return_status
== FILE_RETRY
)
469 return return_status
;
475 while ((*ctx
->stat_func
) (src_path
, &sb
)) {
477 file_error (_(" Cannot stat source file \"%s\" \n %s "),
479 if (return_status
!= FILE_RETRY
)
480 return return_status
;
484 /* Destination already exists */
485 if (sb
.st_dev
== sb2
.st_dev
&& sb
.st_ino
== sb2
.st_ino
)
486 return warn_same_file (_(" `%s' \n and \n `%s' \n are the same file "),
488 /* Should we replace destination? */
491 return_status
= query_replace (ctx
, dst_path
, &sb
, &sb2
);
492 if (return_status
!= FILE_CONT
)
493 return return_status
;
497 if (!ctx
->do_append
) {
498 /* Check the hardlinks */
499 if (!ctx
->follow_links
&& sb
.st_nlink
> 1 &&
500 check_hardlinks (src_path
, dst_path
, &sb
) == 1) {
501 /* We have made a hardlink - no more processing is necessary */
505 if (S_ISLNK (sb
.st_mode
))
506 return make_symlink (ctx
, src_path
, dst_path
);
508 if (S_ISCHR (sb
.st_mode
) || S_ISBLK (sb
.st_mode
) ||
509 S_ISFIFO (sb
.st_mode
) || S_ISNAM (sb
.st_mode
) ||
510 S_ISSOCK (sb
.st_mode
)) {
511 while (mc_mknod (dst_path
, sb
.st_mode
& ctx
->umask_kill
,
513 return_status
= file_error (
514 _(" Cannot create special file \"%s\" \n %s "), dst_path
);
515 if (return_status
== FILE_RETRY
)
517 return return_status
;
521 while (ctx
->preserve_uidgid
522 && mc_chown (dst_path
, sb
.st_uid
, sb
.st_gid
)) {
523 temp_status
= file_error (
524 _(" Cannot chown target file \"%s\" \n %s "), dst_path
);
525 if (temp_status
== FILE_RETRY
)
529 while (ctx
->preserve
&&
530 mc_chmod (dst_path
, sb
.st_mode
& ctx
->umask_kill
)) {
531 temp_status
= file_error (
532 _(" Cannot chmod target file \"%s\" \n %s "), dst_path
);
533 if (temp_status
== FILE_RETRY
)
541 gettimeofday (&tv_transfer_start
, (struct timezone
*) NULL
);
543 while ((src_desc
= mc_open (src_path
, O_RDONLY
| O_LINEAR
)) < 0) {
544 return_status
= file_error (
545 _(" Cannot open source file \"%s\" \n %s "), src_path
);
546 if (return_status
== FILE_RETRY
)
549 return return_status
;
553 if (mc_lseek (src_desc
, ctx
->do_reget
, SEEK_SET
) != ctx
->do_reget
) {
554 message (D_ERROR
, _("Warning"),
555 _(" Reget failed, about to overwrite file "));
556 ctx
->do_reget
= ctx
->do_append
= 0;
560 while (mc_fstat (src_desc
, &sb
)) {
561 return_status
= file_error (
562 _(" Cannot fstat source file \"%s\" \n %s "), src_path
);
563 if (return_status
== FILE_RETRY
)
568 src_mode
= sb
.st_mode
;
571 utb
.actime
= sb
.st_atime
;
572 utb
.modtime
= sb
.st_mtime
;
573 file_size
= sb
.st_size
;
575 open_flags
= O_WRONLY
;
576 if (dst_exists
!= 0) {
577 if (ctx
->do_append
!= 0)
578 open_flags
|= O_APPEND
;
580 open_flags
|= O_CREAT
| O_TRUNC
;
582 open_flags
|= O_CREAT
| O_EXCL
;
584 while ((dest_desc
= mc_open (dst_path
, open_flags
, src_mode
)) < 0) {
585 if (errno
== EEXIST
) {
588 return_status
= file_error (
589 _(" Cannot create target file \"%s\" \n %s "), dst_path
);
590 if (return_status
== FILE_RETRY
)
595 dst_status
= DEST_SHORT
; /* file opened, but not fully copied */
597 appending
= ctx
->do_append
;
600 /* Find out the optimal buffer size. */
601 while (mc_fstat (dest_desc
, &sb
)) {
602 return_status
= file_error (
603 _(" Cannot fstat target file \"%s\" \n %s "), dst_path
);
604 if (return_status
== FILE_RETRY
)
608 buf
= g_malloc (buf_size
);
613 return_status
= file_progress_show (ctx
, 0, file_size
);
617 if (return_status
!= FILE_CONT
)
621 struct timeval tv_current
, tv_last_update
, tv_last_input
;
622 int secs
, update_secs
;
624 const char *stalled_msg
;
626 tv_last_update
= tv_transfer_start
;
630 if (mc_ctl (src_desc
, VFS_CTL_IS_NOTREADY
, 0))
633 while ((n_read
= mc_read (src_desc
, buf
, buf_size
)) < 0) {
634 return_status
= file_error (
635 _(" Cannot read source file \"%s\" \n %s "), src_path
);
636 if (return_status
== FILE_RETRY
)
643 gettimeofday (&tv_current
, NULL
);
647 n_read_total
+= n_read
;
649 /* Windows NT ftp servers report that files have no
650 * permissions: -------, so if we happen to have actually
651 * read something, we should fix the permissions.
653 if (!(src_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
)))
654 src_mode
= S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
;
655 gettimeofday (&tv_last_input
, NULL
);
659 mc_write (dest_desc
, t
, n_read
)) < n_read
) {
666 file_error (_(" Cannot write target file \"%s\" \n %s "),
668 if (return_status
!= FILE_RETRY
)
673 /* 1. Update rotating dash after some time (hardcoded to 2 seconds) */
674 secs
= (tv_current
.tv_sec
- tv_last_update
.tv_sec
);
677 tv_last_update
= tv_current
;
680 /* 2. Check for a stalled condition */
681 update_secs
= (tv_current
.tv_sec
- tv_last_input
.tv_sec
);
683 if (update_secs
> 4) {
684 stalled_msg
= _("(stalled)");
689 dt
= (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
693 ((dt
/ (double) n_read_total
) * file_size
) - dt
;
694 ctx
->bps
= n_read_total
/ ((dt
< 1) ? 1 : dt
);
699 /* 4. Compute BPS rate */
702 (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
703 if (ctx
->bps_time
< 1)
705 ctx
->bps
= n_read_total
/ ctx
->bps_time
;
708 file_progress_set_stalled_label (ctx
, stalled_msg
);
709 return_status
= file_progress_show_bytes (ctx
, *progress_bytes
+
710 n_read_total
+ ctx
->do_reget
, ctx
->progress_bytes
);
711 if (return_status
== FILE_CONT
) {
713 file_progress_show (ctx
, n_read_total
+ ctx
->do_reget
, file_size
);
716 if (return_status
!= FILE_CONT
)
721 dst_status
= DEST_FULL
; /* copy successful, don't remove target file */
726 while (src_desc
!= -1 && mc_close (src_desc
) < 0) {
727 temp_status
= file_error (
728 _(" Cannot close source file \"%s\" \n %s "), src_path
);
729 if (temp_status
== FILE_RETRY
)
731 if (temp_status
== FILE_ABORT
)
732 return_status
= temp_status
;
736 while (dest_desc
!= -1 && mc_close (dest_desc
) < 0) {
737 temp_status
= file_error (
738 _(" Cannot close target file \"%s\" \n %s "), dst_path
);
739 if (temp_status
== FILE_RETRY
)
741 return_status
= temp_status
;
745 if (dst_status
== DEST_SHORT
) {
746 /* Remove short file */
748 result
= query_dialog (Q_("DialogTitle|Copy"),
749 _("Incomplete file was retrieved. Keep it?"),
750 D_ERROR
, 2, _("&Delete"), _("&Keep"));
752 mc_unlink (dst_path
);
753 } else if (dst_status
== DEST_FULL
) {
754 /* Copy has succeeded */
755 if (!appending
&& ctx
->preserve_uidgid
) {
756 while (mc_chown (dst_path
, src_uid
, src_gid
)) {
757 temp_status
= file_error (
758 _(" Cannot chown target file \"%s\" \n %s "), dst_path
);
759 if (temp_status
== FILE_RETRY
)
761 return_status
= temp_status
;
768 while (mc_chmod (dst_path
, (src_mode
& ctx
->umask_kill
))) {
769 temp_status
= file_error (
770 _(" Cannot chmod target file \"%s\" \n %s "), dst_path
);
771 if (temp_status
!= FILE_RETRY
) {
772 return_status
= temp_status
;
777 src_mode
= umask(-1);
779 src_mode
= 0100666 & ~src_mode
;
780 mc_chmod (dst_path
, (src_mode
& ctx
->umask_kill
));
782 mc_utime (dst_path
, &utb
);
786 if (return_status
== FILE_CONT
)
788 progress_update_one (ctx
, progress_count
, progress_bytes
,
789 file_size
, is_toplevel_file
);
791 return return_status
;
795 * I think these copy_*_* functions should have a return type.
796 * anyway, this function *must* have two directories as arguments.
798 /* FIXME: This function needs to check the return values of the
801 copy_dir_dir (FileOpContext
*ctx
, const char *s
, const char *_d
, int toplevel
,
802 int move_over
, int delete, struct link
*parent_dirs
,
803 off_t
*progress_count
, double *progress_bytes
)
806 struct stat buf
, cbuf
;
808 char *dest_dir
= NULL
;
809 FileProgressStatus return_status
= FILE_CONT
;
814 d
= strutils_shell_unescape (_d
);
816 /* First get the mode of the source dir */
818 if ((*ctx
->stat_func
) (s
, &cbuf
)) {
820 file_error (_(" Cannot stat source directory \"%s\" \n %s "), s
);
821 if (return_status
== FILE_RETRY
)
826 if (is_in_linklist (dest_dirs
, s
, &cbuf
)) {
827 /* Don't copy a directory we created before (we don't want to copy
828 infinitely if a directory is copied into itself) */
829 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
830 return_status
= FILE_CONT
;
834 /* Hmm, hardlink to directory??? - Norbert */
835 /* FIXME: In this step we should do something
836 in case the destination already exist */
837 /* Check the hardlinks */
838 if (ctx
->preserve
&& cbuf
.st_nlink
> 1
839 && check_hardlinks (s
, d
, &cbuf
) == 1) {
840 /* We have made a hardlink - no more processing is necessary */
844 if (!S_ISDIR (cbuf
.st_mode
)) {
846 file_error (_(" Source \"%s\" is not a directory \n %s "), s
);
847 if (return_status
== FILE_RETRY
)
852 if (is_in_linklist (parent_dirs
, s
, &cbuf
)) {
853 /* we found a cyclic symbolic link */
854 message (D_ERROR
, MSG_ERROR
,
855 _(" Cannot copy cyclic symbolic link \n `%s' "), s
);
856 return_status
= FILE_SKIP
;
860 lp
= g_new (struct link
, 1);
861 lp
->vfs
= vfs_get_class (s
);
862 lp
->ino
= cbuf
.st_ino
;
863 lp
->dev
= cbuf
.st_dev
;
864 lp
->next
= parent_dirs
;
868 /* Now, check if the dest dir exists, if not, create it. */
869 if (mc_stat (d
, &buf
)) {
870 /* Here the dir doesn't exist : make it ! */
872 if (mc_rename (s
, d
) == 0) {
873 return_status
= FILE_CONT
;
881 * If the destination directory exists, we want to copy the whole
882 * directory, but we only want this to happen once.
884 * Escape sequences added to the * to compiler warnings.
885 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
886 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
888 if (!S_ISDIR (buf
.st_mode
)) {
889 return_status
= file_error(
890 _(" Destination \"%s\" must be a directory \n %s "), d
);
891 if (return_status
== FILE_RETRY
)
895 /* Dive into subdir if exists */
896 if (toplevel
&& ctx
->dive_into_subdirs
) {
897 dest_dir
= concat_dir_and_file (d
, x_basename (s
));
904 while (my_mkdir (dest_dir
, (cbuf
.st_mode
& ctx
->umask_kill
) | S_IRWXU
)) {
905 return_status
= file_error (
906 _(" Cannot create target directory \"%s\" \n %s "), dest_dir
);
907 if (return_status
!= FILE_RETRY
)
911 lp
= g_new (struct link
, 1);
912 mc_stat (dest_dir
, &buf
);
913 lp
->vfs
= vfs_get_class (dest_dir
);
914 lp
->ino
= buf
.st_ino
;
915 lp
->dev
= buf
.st_dev
;
916 lp
->next
= dest_dirs
;
919 if (ctx
->preserve_uidgid
) {
920 while (mc_chown (dest_dir
, cbuf
.st_uid
, cbuf
.st_gid
)) {
921 return_status
= file_error (
922 _(" Cannot chown target directory \"%s\" \n %s "), dest_dir
);
923 if (return_status
!= FILE_RETRY
)
929 /* open the source dir for reading */
930 reading
= mc_opendir (s
);
934 while ((next
= mc_readdir (reading
)) && return_status
!= FILE_ABORT
) {
937 * Now, we don't want '.' and '..' to be created / copied at any time
939 if (!strcmp (next
->d_name
, "."))
941 if (!strcmp (next
->d_name
, ".."))
944 /* get the filename and add it to the src directory */
945 path
= concat_dir_and_file (s
, next
->d_name
);
947 (*ctx
->stat_func
) (path
, &buf
);
948 if (S_ISDIR (buf
.st_mode
)) {
951 mdpath
= concat_dir_and_file (dest_dir
, next
->d_name
);
953 * From here, we just intend to recursively copy subdirs, not
954 * the double functionality of copying different when the target
955 * dir already exists. So, we give the recursive call the flag 0
956 * meaning no toplevel.
958 return_status
= copy_dir_dir (ctx
, path
, mdpath
, 0, 0, delete,
959 parent_dirs
, progress_count
, progress_bytes
);
964 dest_file
= concat_dir_and_file (dest_dir
, x_basename (path
));
965 return_status
= copy_file_file (ctx
, path
, dest_file
, 1,
966 progress_count
, progress_bytes
, 0);
969 if (delete && return_status
== FILE_CONT
) {
970 if (ctx
->erase_at_end
) {
971 static struct link
*tail
;
972 size_t len
= strlen (path
);
973 lp
= g_malloc (sizeof (struct link
) + len
);
974 strncpy (lp
->name
, path
, len
+ 1);
975 lp
->st_mode
= buf
.st_mode
;
977 if (erase_list
!= NULL
) {
981 erase_list
= tail
= lp
;
983 if (S_ISDIR (buf
.st_mode
)) {
984 return_status
= erase_dir_iff_empty (ctx
, path
);
986 return_status
= erase_file (ctx
, path
, 0, 0, 0);
991 mc_closedir (reading
);
994 mc_chmod (dest_dir
, cbuf
.st_mode
& ctx
->umask_kill
);
995 utb
.actime
= cbuf
.st_atime
;
996 utb
.modtime
= cbuf
.st_mtime
;
997 mc_utime (dest_dir
, &utb
);
999 cbuf
.st_mode
= umask(-1);
1000 umask(cbuf
.st_mode
);
1001 cbuf
.st_mode
= 0100777 & ~cbuf
.st_mode
;
1002 mc_chmod (dest_dir
, cbuf
.st_mode
& ctx
->umask_kill
);
1007 g_free (parent_dirs
);
1010 return return_status
;
1015 /* {{{ Move routines */
1017 static FileProgressStatus
1018 move_file_file (FileOpContext
*ctx
, const char *s
, const char *d
,
1019 off_t
*progress_count
, double *progress_bytes
)
1021 struct stat src_stats
, dst_stats
;
1022 FileProgressStatus return_status
= FILE_CONT
;
1023 gboolean copy_done
= FALSE
;
1025 if (file_progress_show_source (ctx
, s
) == FILE_ABORT
1026 || file_progress_show_target (ctx
, d
) == FILE_ABORT
)
1031 while (mc_lstat (s
, &src_stats
) != 0) {
1032 /* Source doesn't exist */
1034 file_error (_(" Cannot stat file \"%s\" \n %s "), s
);
1035 if (return_status
!= FILE_RETRY
)
1036 return return_status
;
1039 if (mc_lstat (d
, &dst_stats
) == 0) {
1040 if (src_stats
.st_dev
== dst_stats
.st_dev
1041 && src_stats
.st_ino
== dst_stats
.st_ino
)
1042 return warn_same_file (_(" `%s' \n and \n `%s' \n are the same file "), s
, d
);
1044 if (S_ISDIR (dst_stats
.st_mode
)) {
1045 message (D_ERROR
, MSG_ERROR
,
1046 _(" Cannot overwrite directory `%s' "), d
);
1051 if (confirm_overwrite
) {
1052 return_status
= query_replace (ctx
, d
, &src_stats
, &dst_stats
);
1053 if (return_status
!= FILE_CONT
)
1054 return return_status
;
1056 /* Ok to overwrite */
1059 if (!ctx
->do_append
) {
1060 if (S_ISLNK (src_stats
.st_mode
) && ctx
->stable_symlinks
) {
1061 if ((return_status
= make_symlink (ctx
, s
, d
)) == FILE_CONT
) {
1062 goto retry_src_remove
;
1064 return return_status
;
1067 if (mc_rename (s
, d
) == 0) {
1068 return progress_update_one (ctx
, progress_count
,
1070 src_stats
.st_size
, 1);
1074 /* Comparison to EXDEV seems not to work in nfs if you're moving from
1075 one nfs to the same, but on the server it is on two different
1076 filesystems. Then nfs returns EIO instead of EXDEV.
1077 Hope it will not hurt if we always in case of error try to copy/delete. */
1079 errno
= EXDEV
; /* Hack to copy (append) the file and then delete it */
1081 if (errno
!= EXDEV
) {
1083 files_error (_(" Cannot move file \"%s\" to \"%s\" \n %s "), s
,
1085 if (return_status
== FILE_RETRY
)
1087 return return_status
;
1091 /* Failed because filesystem boundary -> copy the file instead */
1093 copy_file_file (ctx
, s
, d
, 0, progress_count
, progress_bytes
, 1);
1094 if (return_status
!= FILE_CONT
)
1095 return return_status
;
1099 if ((return_status
=
1100 file_progress_show_source (ctx
, NULL
)) != FILE_CONT
1101 || (return_status
= file_progress_show (ctx
, 0, 0)) != FILE_CONT
)
1102 return return_status
;
1107 if (mc_unlink (s
)) {
1109 file_error (_(" Cannot remove file \"%s\" \n %s "), s
);
1110 if (return_status
== FILE_RETRY
)
1111 goto retry_src_remove
;
1112 return return_status
;
1116 return_status
= progress_update_one (ctx
,
1119 src_stats
.st_size
, 1);
1122 return return_status
;
1126 move_dir_dir (FileOpContext
*ctx
, const char *s
, const char *d
,
1127 off_t
*progress_count
, double *progress_bytes
)
1129 struct stat sbuf
, dbuf
, destbuf
;
1132 FileProgressStatus return_status
;
1133 gboolean move_over
= FALSE
;
1136 if (file_progress_show_source (ctx
, s
) == FILE_ABORT
||
1137 file_progress_show_target (ctx
, d
) == FILE_ABORT
)
1143 dstat_ok
= (mc_stat (d
, &dbuf
) == 0);
1145 if (dstat_ok
&& sbuf
.st_dev
== dbuf
.st_dev
&& sbuf
.st_ino
== dbuf
.st_ino
)
1146 return warn_same_file (_(" `%s' \n and \n `%s' \n are the same directory "), s
, d
);
1149 destdir
= g_strdup (d
); /* destination doesn't exist */
1150 else if (!ctx
->dive_into_subdirs
) {
1151 destdir
= g_strdup (d
);
1154 destdir
= concat_dir_and_file (d
, x_basename (s
));
1156 /* Check if the user inputted an existing dir */
1158 if (!mc_stat (destdir
, &destbuf
)) {
1160 return_status
= copy_dir_dir (ctx
, s
, destdir
, 0, 1, 1, 0,
1161 progress_count
, progress_bytes
);
1163 if (return_status
!= FILE_CONT
)
1167 if (S_ISDIR (destbuf
.st_mode
))
1170 (" Cannot overwrite directory \"%s\" %s "),
1174 file_error (_(" Cannot overwrite file \"%s\" %s "),
1176 if (return_status
== FILE_RETRY
)
1177 goto retry_dst_stat
;
1180 return return_status
;
1184 if (mc_rename (s
, destdir
) == 0) {
1185 return_status
= FILE_CONT
;
1189 if (errno
!= EXDEV
) {
1192 (" Cannot move directory \"%s\" to \"%s\" \n %s "),
1194 if (return_status
== FILE_RETRY
)
1198 /* Failed because of filesystem boundary -> copy dir instead */
1200 copy_dir_dir (ctx
, s
, destdir
, 0, 0, 1, 0, progress_count
,
1203 if (return_status
!= FILE_CONT
)
1206 if ((return_status
=
1207 file_progress_show_source (ctx
, NULL
)) != FILE_CONT
1208 || (return_status
= file_progress_show (ctx
, 0, 0)) != FILE_CONT
)
1212 if (ctx
->erase_at_end
) {
1213 for (; erase_list
&& return_status
!= FILE_ABORT
;) {
1214 if (S_ISDIR (erase_list
->st_mode
)) {
1216 erase_dir_iff_empty (ctx
, erase_list
->name
);
1219 erase_file (ctx
, erase_list
->name
, 0, 0, 0);
1221 erase_list
= erase_list
->next
;
1225 erase_dir_iff_empty (ctx
, s
);
1229 while (erase_list
) {
1231 erase_list
= erase_list
->next
;
1234 return return_status
;
1239 /* {{{ Erase routines */
1240 /* Don't update progress status if progress_count==NULL */
1241 static FileProgressStatus
1242 erase_file (FileOpContext
*ctx
, const char *s
, off_t
*progress_count
,
1243 double *progress_bytes
, int is_toplevel_file
)
1248 if (file_progress_show_deleting (ctx
, s
) == FILE_ABORT
)
1252 if (progress_count
&& mc_lstat (s
, &buf
)) {
1253 /* ignore, most likely the mc_unlink fails, too */
1257 while (mc_unlink (s
)) {
1259 file_error (_(" Cannot delete file \"%s\" \n %s "), s
);
1260 if (return_status
!= FILE_RETRY
)
1261 return return_status
;
1265 return progress_update_one (ctx
, progress_count
, progress_bytes
,
1266 buf
.st_size
, is_toplevel_file
);
1271 static FileProgressStatus
1272 recursive_erase (FileOpContext
*ctx
, const char *s
, off_t
*progress_count
,
1273 double *progress_bytes
)
1275 struct dirent
*next
;
1279 FileProgressStatus return_status
= FILE_CONT
;
1281 if (!strcmp (s
, ".."))
1284 reading
= mc_opendir (s
);
1289 while ((next
= mc_readdir (reading
)) && return_status
== FILE_CONT
) {
1290 if (!strcmp (next
->d_name
, "."))
1292 if (!strcmp (next
->d_name
, ".."))
1294 path
= concat_dir_and_file (s
, next
->d_name
);
1295 if (mc_lstat (path
, &buf
)) {
1297 mc_closedir (reading
);
1300 if (S_ISDIR (buf
.st_mode
))
1303 (ctx
, path
, progress_count
, progress_bytes
)
1304 != FILE_CONT
) ? FILE_RETRY
: FILE_CONT
;
1307 erase_file (ctx
, path
, progress_count
, progress_bytes
, 0);
1310 mc_closedir (reading
);
1311 if (return_status
!= FILE_CONT
)
1312 return return_status
;
1313 if (file_progress_show_deleting (ctx
, s
) == FILE_ABORT
)
1317 while (my_rmdir (s
)) {
1319 file_error (_(" Cannot remove directory \"%s\" \n %s "), s
);
1320 if (return_status
!= FILE_RETRY
)
1321 return return_status
;
1327 /* Return -1 on error, 1 if there are no entries besides "." and ".."
1328 in the directory path points to, 0 else. */
1330 check_dir_is_empty (const char *path
)
1336 dir
= mc_opendir (path
);
1340 for (i
= 1, d
= mc_readdir (dir
); d
; d
= mc_readdir (dir
)) {
1341 if (d
->d_name
[0] == '.' && (d
->d_name
[1] == '\0' ||
1342 (d
->d_name
[1] == '.'
1343 && d
->d_name
[2] == '\0')))
1344 continue; /* "." or ".." */
1354 erase_dir (FileOpContext
*ctx
, const char *s
, off_t
*progress_count
,
1355 double *progress_bytes
)
1357 FileProgressStatus error
;
1359 if (strcmp (s
, "..") == 0)
1362 if (strcmp (s
, ".") == 0)
1365 if (file_progress_show_deleting (ctx
, s
) == FILE_ABORT
)
1369 /* The old way to detect a non empty directory was:
1370 error = my_rmdir (s);
1371 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
1372 For the linux user space nfs server (nfs-server-2.2beta29-2)
1373 we would have to check also for EIO. I hope the new way is
1374 fool proof. (Norbert)
1376 error
= check_dir_is_empty (s
);
1377 if (error
== 0) { /* not empty */
1378 error
= query_recursive (ctx
, s
);
1379 if (error
== FILE_CONT
)
1380 return recursive_erase (ctx
, s
, progress_count
,
1386 while (my_rmdir (s
) == -1) {
1388 file_error (_(" Cannot remove directory \"%s\" \n %s "), s
);
1389 if (error
!= FILE_RETRY
)
1396 static FileProgressStatus
1397 erase_dir_iff_empty (FileOpContext
*ctx
, const char *s
)
1399 FileProgressStatus error
;
1401 if (strcmp (s
, "..") == 0)
1404 if (strcmp (s
, ".") == 0)
1407 if (file_progress_show_deleting (ctx
, s
) == FILE_ABORT
)
1411 if (1 != check_dir_is_empty (s
)) /* not empty or error */
1414 while (my_rmdir (s
)) {
1416 file_error (_(" Cannot remove directory \"%s\" \n %s "), s
);
1417 if (error
!= FILE_RETRY
)
1426 /* {{{ Panel operate routines */
1429 * Return currently selected entry name or the name of the first marked
1430 * entry if there is one.
1433 panel_get_file (WPanel
*panel
, struct stat
*stat_buf
)
1437 if (get_current_type () == view_tree
) {
1438 WTree
*tree
= (WTree
*) get_panel_widget (get_current_index ());
1439 char *tree_name
= tree_selected_name (tree
);
1441 mc_stat (tree_name
, stat_buf
);
1445 if (panel
->marked
) {
1446 for (i
= 0; i
< panel
->count
; i
++)
1447 if (panel
->dir
.list
[i
].f
.marked
) {
1448 *stat_buf
= panel
->dir
.list
[i
].st
;
1449 return panel
->dir
.list
[i
].fname
;
1452 *stat_buf
= panel
->dir
.list
[panel
->selected
].st
;
1453 return panel
->dir
.list
[panel
->selected
].fname
;
1455 g_assert_not_reached ();
1461 compute_dir_size_create_ui (void)
1463 ComputeDirSizeUI
*ui
;
1465 const char *b_name
= N_("&Abort");
1471 ui
= g_new (ComputeDirSizeUI
, 1);
1473 ui
->dlg
= create_dlg (0, 0, 8, COLS
/2, dialog_colors
, NULL
,
1474 NULL
, _("Directory scanning"), DLG_CENTER
);
1475 ui
->dirname
= label_new (3, 3, "");
1476 add_widget (ui
->dlg
, ui
->dirname
);
1478 add_widget (ui
->dlg
,
1479 button_new (5, (ui
->dlg
->cols
- strlen (b_name
))/2,
1480 FILE_ABORT
, NORMAL_BUTTON
, b_name
, NULL
));
1482 /* We will manage the dialog without any help,
1483 that's why we have to call init_dlg */
1490 compute_dir_size_destroy_ui (ComputeDirSizeUI
*ui
)
1493 /* schedule to update passive panel */
1494 other_panel
->dirty
= 1;
1496 /* close and destroy dialog */
1497 dlg_run_done (ui
->dlg
);
1498 destroy_dlg (ui
->dlg
);
1504 compute_dir_size_update_ui (const void *ui
, const char *dirname
)
1506 const ComputeDirSizeUI
*this = (const ComputeDirSizeUI
*) ui
;
1513 label_set_text (this->dirname
, name_trunc (dirname
, this->dlg
->cols
- 6));
1515 event
.x
= -1; /* Don't show the GPM cursor */
1516 c
= tty_get_event (&event
, FALSE
, FALSE
);
1520 /* Reinitialize to avoid old values after events other than
1521 selecting a button */
1522 this->dlg
->ret_value
= FILE_CONT
;
1524 dlg_process_event (this->dlg
, c
, &event
);
1526 switch (this->dlg
->ret_value
) {
1538 * Computes the number of bytes used by the files in a directory
1541 compute_dir_size (const char *dirname
, const void *ui
,
1542 compute_dir_size_callback cback
,
1543 off_t
*ret_marked
, double *ret_total
)
1546 struct dirent
*dirent
;
1547 FileProgressStatus ret
= FILE_CONT
;
1549 dir
= mc_opendir (dirname
);
1554 while ((dirent
= mc_readdir (dir
)) != NULL
) {
1559 ret
= (cback
!= NULL
) ? cback (ui
, dirname
) : FILE_CONT
;
1561 if (ret
!= FILE_CONT
)
1564 if (strcmp (dirent
->d_name
, ".") == 0)
1566 if (strcmp (dirent
->d_name
, "..") == 0)
1569 fullname
= concat_dir_and_file (dirname
, dirent
->d_name
);
1570 res
= mc_lstat (fullname
, &s
);
1577 if (S_ISDIR (s
.st_mode
)) {
1578 off_t subdir_count
= 0;
1579 double subdir_bytes
= 0;
1581 ret
= compute_dir_size (fullname
, ui
, cback
, &subdir_count
, &subdir_bytes
);
1583 if (ret
!= FILE_CONT
) {
1588 *ret_marked
+= subdir_count
;
1589 *ret_total
+= subdir_bytes
;
1592 *ret_total
+= s
.st_size
;
1604 * panel_compute_totals:
1606 * compute the number of files and the number of bytes
1607 * used up by the whole selection, recursing directories
1608 * as required. In addition, it checks to see if it will
1609 * overwrite any files by doing the copy.
1611 static FileProgressStatus
1612 panel_compute_totals (WPanel
*panel
, const void *ui
,
1613 compute_dir_size_callback cback
,
1614 off_t
*ret_marked
, double *ret_total
)
1621 for (i
= 0; i
< panel
->count
; i
++) {
1624 if (!panel
->dir
.list
[i
].f
.marked
)
1627 s
= &panel
->dir
.list
[i
].st
;
1629 if (S_ISDIR (s
->st_mode
)) {
1631 off_t subdir_count
= 0;
1632 double subdir_bytes
= 0;
1633 FileProgressStatus status
;
1636 concat_dir_and_file (panel
->cwd
, panel
->dir
.list
[i
].fname
);
1638 status
= compute_dir_size (dir_name
, ui
, cback
,
1639 &subdir_count
, &subdir_bytes
);
1642 if (status
!= FILE_CONT
)
1645 *ret_marked
+= subdir_count
;
1646 *ret_total
+= subdir_bytes
;
1649 *ret_total
+= s
->st_size
;
1657 * This array introduced to avoid translation problems. The former (op_names)
1658 * is assumed to be nouns, suitable in dialog box titles; this one should
1659 * contain whatever is used in prompt itself (i.e. in russian, it's verb).
1660 * (I don't use spaces around the words, because someday they could be
1661 * dropped, when widgets get smarter)
1664 /* TRANSLATORS: no need to translate 'FileOperation', it's just a context prefix */
1665 static const char *op_names1
[] = {
1666 N_("FileOperation|Copy"),
1667 N_("FileOperation|Move"),
1668 N_("FileOperation|Delete")
1672 * These are formats for building a prompt. Parts encoded as follows:
1673 * %o - operation from op_names1
1674 * %f - file/files or files/directories, as appropriate
1675 * %m - "with source mask" or question mark for delete
1676 * %s - source name (truncated)
1677 * %d - number of marked files
1678 * %e - "to:" or question mark for delete
1680 * xgettext:no-c-format */
1681 static const char *one_format
= N_("%o %f \"%s\"%m");
1682 /* xgettext:no-c-format */
1683 static const char *many_format
= N_("%o %d %f%m");
1685 static const char *prompt_parts
[] = {
1690 N_("files/directories"),
1691 N_(" with source mask:"),
1695 static const char *question_format
= N_("%s?");
1698 * Generate user prompt for panel operation.
1699 * single_source is the name if the source entry or NULL for multiple
1701 * src_stat is only used when single_source is not NULL.
1704 panel_operate_generate_prompt (const WPanel
*panel
, const int operation
,
1705 gboolean single_source
,
1706 const struct stat
*src_stat
)
1708 const char *sp
, *cp
;
1709 char format_string
[BUF_MEDIUM
];
1710 char *dp
= format_string
;
1711 gboolean build_question
= FALSE
;
1714 static gboolean i18n_flag
= FALSE
;
1718 for (i
= sizeof (op_names1
) / sizeof (op_names1
[0]); i
--;)
1719 op_names1
[i
] = Q_(op_names1
[i
]);
1721 for (i
= sizeof (prompt_parts
) / sizeof (prompt_parts
[0]); i
--;)
1722 prompt_parts
[i
] = _(prompt_parts
[i
]);
1724 one_format
= _(one_format
);
1725 many_format
= _(many_format
);
1726 question_format
= _(question_format
);
1729 #endif /* ENABLE_NLS */
1731 sp
= single_source
? one_format
: many_format
;
1733 while (*sp
!= '\0') {
1739 cp
= op_names1
[operation
];
1742 if (operation
== OP_DELETE
) {
1744 build_question
= TRUE
;
1746 cp
= prompt_parts
[5];
1749 if (operation
== OP_DELETE
) {
1751 build_question
= TRUE
;
1753 cp
= prompt_parts
[6];
1756 if (single_source
) {
1757 cp
= S_ISDIR (src_stat
->
1758 st_mode
) ? prompt_parts
[2] : prompt_parts
[0];
1760 cp
= (panel
->marked
== panel
->dirs_marked
)
1762 : (panel
->dirs_marked
? prompt_parts
[4] : prompt_parts
[1]);
1780 if (build_question
) {
1781 char tmp
[BUF_MEDIUM
];
1783 memmove (tmp
, format_string
, sizeof (tmp
));
1784 g_snprintf (format_string
, sizeof (format_string
),
1785 question_format
, tmp
);
1788 return g_strdup (format_string
);
1791 #ifdef WITH_BACKGROUND
1793 end_bg_process (FileOpContext
*ctx
, enum OperationMode mode
) {
1799 unregister_task_with_pid(pid
);
1800 // file_op_context_destroy(ctx);
1808 * Performs one of the operations on the selection on the source_panel
1809 * (copy, delete, move).
1811 * Returns 1 if did change the directory
1812 * structure, Returns 0 if user aborted
1814 * force_single forces operation on the current entry and affects
1815 * default destination. Current filename is used as default.
1818 panel_operate (void *source_panel
, FileOperation operation
,
1821 WPanel
*panel
= (WPanel
*) source_panel
;
1822 const gboolean single_entry
= force_single
|| (panel
->marked
<= 1)
1823 || (get_current_type () == view_tree
);
1825 char *source
= NULL
;
1826 #ifdef WITH_FULL_PATHS
1827 char *source_with_path
= NULL
;
1829 # define source_with_path source
1830 #endif /* !WITH_FULL_PATHS */
1833 char *save_cwd
= NULL
, *save_dest
= NULL
;
1834 struct stat src_stat
, dst_stat
;
1836 FileProgressStatus value
;
1843 int do_bg
= 0; /* do background operation? */
1846 static gboolean i18n_flag
= FALSE
;
1848 for (i
= sizeof (op_names1
) / sizeof (op_names1
[0]); i
--;)
1849 op_names
[i
] = Q_(op_names
[i
]);
1852 #endif /* ENABLE_NLS */
1854 free_linklist (&linklist
);
1855 free_linklist (&dest_dirs
);
1857 /* Update panel contents to avoid actions on deleted files */
1858 if (!panel
->is_panelized
) {
1859 update_panels (UP_RELOAD
, UP_KEEPSEL
);
1865 source
= selection (panel
)->fname
;
1866 src_stat
= selection (panel
)->st
;
1868 source
= panel_get_file (panel
, &src_stat
);
1871 if (!strcmp (source
, "..")) {
1872 message (D_ERROR
, MSG_ERROR
, _(" Cannot operate on \"..\"! "));
1877 ctx
= file_op_context_new (operation
);
1879 /* Show confirmation dialog */
1880 if (operation
!= OP_DELETE
) {
1885 /* Forced single operations default to the original name */
1888 else if (get_other_type () == view_listing
)
1889 dest_dir
= other_panel
->cwd
;
1891 dest_dir
= panel
->cwd
;
1893 * Add trailing backslash only when do non-local ops.
1894 * It saves user from occasional file renames (when destination
1898 && dest_dir
[0] != '\0'
1899 && dest_dir
[strlen (dest_dir
) - 1] != PATH_SEP
) {
1900 /* add trailing separator */
1901 dest_dir_
= g_strconcat (dest_dir
, PATH_SEP_STR
, (char *) NULL
);
1904 dest_dir_
= g_strdup (dest_dir
);
1906 if (dest_dir_
== NULL
) {
1907 file_op_context_destroy (ctx
);
1911 /* Generate confirmation prompt */
1912 format
= panel_operate_generate_prompt (panel
, operation
,
1913 source
!= NULL
, &src_stat
);
1915 dest
= file_mask_dialog (ctx
, operation
, source
!= NULL
, format
,
1916 source
!= NULL
? (void *) source
1917 : (void *) &panel
->marked
,
1923 if (dest
== NULL
|| dest
[0] == '\0') {
1924 file_op_context_destroy (ctx
);
1928 } else if (confirm_delete
) {
1930 char fmd_buf
[BUF_MEDIUM
];
1932 /* Generate confirmation prompt */
1933 format
= panel_operate_generate_prompt (panel
, OP_DELETE
,
1934 source
!= NULL
, &src_stat
);
1937 g_snprintf (fmd_buf
, sizeof (fmd_buf
), format
, panel
->marked
);
1939 const int fmd_xlen
= 64;
1940 i
= fmd_xlen
- str_term_width1 (format
) - 4;
1941 g_snprintf (fmd_buf
, sizeof (fmd_buf
),
1942 format
, str_trunc (source
, i
));
1950 i
= query_dialog (op_names
[operation
], fmd_buf
, D_ERROR
, 2,
1951 _("&Yes"), _("&No"));
1954 file_op_context_destroy (ctx
);
1959 /* Background also need ctx->ui, but not full */
1961 file_op_context_create_ui_without_init (ctx
, 1);
1963 file_op_context_create_ui (ctx
, 1);
1965 #ifdef WITH_BACKGROUND
1966 /* Did the user select to do a background operation? */
1970 v
= do_background (ctx
,
1971 g_strconcat (op_names
[operation
], ": ",
1972 panel
->cwd
, (char *) NULL
));
1974 message (D_ERROR
, MSG_ERROR
,
1975 _(" Sorry, I could not put the job in background "));
1978 /* If we are the parent */
1980 mc_setctl (panel
->cwd
, VFS_SETCTL_FORGET
, NULL
);
1981 mc_setctl (dest
, VFS_SETCTL_FORGET
, NULL
);
1982 /* file_op_context_destroy (ctx); */
1986 #endif /* WITH_BACKGROUND */
1988 /* Initialize things */
1989 /* We do not want to trash cache every time file is
1990 created/touched. However, this will make our cache contain
1993 if (mc_setctl (dest
, VFS_SETCTL_STALE_DATA
, (void *) 1))
1994 save_dest
= g_strdup (dest
);
1997 if (mc_setctl (panel
->cwd
, VFS_SETCTL_STALE_DATA
, (void *) 1))
1998 save_cwd
= g_strdup (panel
->cwd
);
2001 /* Now, let's do the job */
2003 /* This code is only called by the tree and panel code */
2005 /* We now have ETA in all cases */
2007 /* One file: FIXME mc_chdir will take user out of any vfs */
2008 if (operation
!= OP_COPY
&& get_current_type () == view_tree
)
2009 mc_chdir (PATH_SEP_STR
);
2011 /* The source and src_stat variables have been initialized before */
2012 #ifdef WITH_FULL_PATHS
2013 source_with_path
= concat_dir_and_file (panel
->cwd
, source
);
2014 #endif /* WITH_FULL_PATHS */
2016 if (operation
== OP_DELETE
) {
2017 if (S_ISDIR (src_stat
.st_mode
))
2018 value
= erase_dir (ctx
, source_with_path
, &count
, &bytes
);
2021 erase_file (ctx
, source_with_path
, &count
, &bytes
, 1);
2023 temp
= transform_source (ctx
, source_with_path
);
2025 value
= transform_error
;
2027 char *repl_dest
= mc_search_prepare_replace_str2 (ctx
->search_handle
, dest
);
2028 char *temp2
= concat_dir_and_file (repl_dest
, temp
);
2034 switch (operation
) {
2037 * we use file_mask_op_follow_links only with OP_COPY,
2039 (*ctx
->stat_func
) (source_with_path
, &src_stat
);
2041 if (S_ISDIR (src_stat
.st_mode
)) {
2043 copy_dir_dir (ctx
, source_with_path
, dest
, 1,
2044 0, 0, 0, &count
, &bytes
);
2047 copy_file_file (ctx
, source_with_path
, dest
, 1,
2053 if (S_ISDIR (src_stat
.st_mode
))
2055 move_dir_dir (ctx
, source_with_path
, dest
,
2059 move_file_file (ctx
, source_with_path
, dest
,
2064 /* Unknown file operation */
2068 } /* Copy or move operation */
2070 if ((value
== FILE_CONT
) && !force_single
)
2071 unmark_files (panel
);
2074 /* Check destination for copy or move operation */
2075 if (operation
!= OP_DELETE
) {
2076 retry_many_dst_stat
:
2077 dst_result
= mc_stat (dest
, &dst_stat
);
2078 if (dst_result
== 0 && !S_ISDIR (dst_stat
.st_mode
)) {
2080 (_(" Destination \"%s\" must be a directory \n %s "),
2081 dest
) == FILE_RETRY
)
2082 goto retry_many_dst_stat
;
2087 /* Initialize variables for progress bars */
2088 if (operation
!= OP_MOVE
&& verbose
&& file_op_compute_totals
) {
2089 ComputeDirSizeUI
*ui
;
2090 FileProgressStatus status
;
2092 ui
= compute_dir_size_create_ui ();
2093 status
= panel_compute_totals (panel
,
2094 ui
, compute_dir_size_update_ui
,
2095 &ctx
->progress_count
, &ctx
->progress_bytes
);
2096 compute_dir_size_destroy_ui (ui
);
2098 if (status
!= FILE_CONT
)
2101 ctx
->progress_totals_computed
= 1;
2103 ctx
->progress_totals_computed
= 0;
2104 ctx
->progress_count
= panel
->marked
;
2105 ctx
->progress_bytes
= panel
->total
;
2108 /* Loop for every file, perform the actual copy operation */
2109 for (i
= 0; i
< panel
->count
; i
++) {
2110 if (!panel
->dir
.list
[i
].f
.marked
)
2111 continue; /* Skip the unmarked ones */
2113 source
= panel
->dir
.list
[i
].fname
;
2114 src_stat
= panel
->dir
.list
[i
].st
;
2116 #ifdef WITH_FULL_PATHS
2117 g_free (source_with_path
);
2118 source_with_path
= concat_dir_and_file (panel
->cwd
, source
);
2119 #endif /* WITH_FULL_PATHS */
2121 if (operation
== OP_DELETE
) {
2122 if (S_ISDIR (src_stat
.st_mode
))
2124 erase_dir (ctx
, source_with_path
, &count
, &bytes
);
2127 erase_file (ctx
, source_with_path
, &count
, &bytes
,
2130 temp
= transform_source (ctx
, source_with_path
);
2132 value
= transform_error
;
2135 char *repl_dest
= mc_search_prepare_replace_str2 (ctx
->search_handle
, dest
);
2136 char *temp2
= concat_dir_and_file (repl_dest
, temp
);
2140 temp3
= source_with_path
;
2141 source_with_path
= strutils_shell_unescape(source_with_path
);
2144 temp2
= strutils_shell_unescape(temp2
);
2147 switch (operation
) {
2150 * we use file_mask_op_follow_links only with OP_COPY
2152 (*ctx
->stat_func
) (source_with_path
, &src_stat
);
2153 if (S_ISDIR (src_stat
.st_mode
))
2155 copy_dir_dir (ctx
, source_with_path
, temp2
,
2156 1, 0, 0, 0, &count
, &bytes
);
2159 copy_file_file (ctx
, source_with_path
,
2160 temp2
, 1, &count
, &bytes
,
2162 free_linklist (&dest_dirs
);
2166 if (S_ISDIR (src_stat
.st_mode
))
2168 move_dir_dir (ctx
, source_with_path
, temp2
,
2172 move_file_file (ctx
, source_with_path
,
2173 temp2
, &count
, &bytes
);
2177 /* Unknown file operation */
2182 } /* Copy or move operation */
2184 if (value
== FILE_ABORT
)
2187 if (value
== FILE_CONT
)
2188 do_file_mark (panel
, i
, 0);
2190 if (file_progress_show_count (ctx
, count
, ctx
->progress_count
)
2195 && file_progress_show_bytes (ctx
, bytes
,
2196 ctx
->progress_bytes
) ==
2200 if (operation
!= OP_DELETE
&& verbose
2201 && file_progress_show (ctx
, 0, 0) == FILE_ABORT
)
2205 } /* Loop for every file */
2206 } /* Many entries */
2211 mc_setctl (save_cwd
, VFS_SETCTL_STALE_DATA
, NULL
);
2215 mc_setctl (save_dest
, VFS_SETCTL_STALE_DATA
, NULL
);
2219 free_linklist (&linklist
);
2220 free_linklist (&dest_dirs
);
2221 #ifdef WITH_FULL_PATHS
2222 g_free (source_with_path
);
2223 #endif /* WITH_FULL_PATHS */
2225 g_free (ctx
->dest_mask
);
2226 ctx
->dest_mask
= NULL
;
2227 #ifdef WITH_BACKGROUND
2228 /* Let our parent know we are saying bye bye */
2229 if (we_are_background
) {
2230 int cur_pid
= getpid();
2231 /* Send pid to parent with child context, it is fork and
2232 don't modify real parent ctx */
2234 parent_call ((void *) end_bg_process
, ctx
, 0);
2239 #endif /* WITH_BACKGROUND */
2241 file_op_context_destroy (ctx
);
2247 /* {{{ Query/status report routines */
2249 static FileProgressStatus
2250 real_do_file_error (enum OperationMode mode
, const char *error
)
2255 msg
= mode
== Foreground
? MSG_ERROR
: _(" Background process error ");
2257 query_dialog (msg
, error
, D_ERROR
, 3, _("&Skip"), _("&Retry"),
2275 /* Report error with one file */
2277 file_error (const char *format
, const char *file
)
2279 char buf
[BUF_MEDIUM
];
2281 g_snprintf (buf
, sizeof (buf
), format
,
2282 path_trunc (file
, 30), unix_error_string (errno
));
2284 return do_file_error (buf
);
2287 /* Report error with two files */
2288 static FileProgressStatus
2289 files_error (const char *format
, const char *file1
, const char *file2
)
2291 char buf
[BUF_MEDIUM
];
2292 char *nfile1
= g_strdup (path_trunc (file1
, 15));
2293 char *nfile2
= g_strdup (path_trunc (file2
, 15));
2295 g_snprintf (buf
, sizeof (buf
), format
, nfile1
, nfile2
,
2296 unix_error_string (errno
));
2301 return do_file_error (buf
);
2304 static FileProgressStatus
2305 real_query_recursive (FileOpContext
*ctx
, enum OperationMode mode
, const char *s
)
2309 if (ctx
->recursive_result
< RECURSIVE_ALWAYS
) {
2313 _("\n Directory not empty. \n"
2314 " Delete it recursively? ")
2315 : _("\n Background process: Directory not empty \n"
2316 " Delete it recursively? ");
2317 text
= g_strconcat (_(" Delete: "), path_trunc (s
, 30), " ", (char *) NULL
);
2321 ctx
->recursive_result
= query_dialog (text
, msg
, D_ERROR
, 5,
2322 _("&Yes"), _("&No"),
2323 _("A&ll"), _("Non&e"),
2326 if (ctx
->recursive_result
!= RECURSIVE_ABORT
)
2331 switch (ctx
->recursive_result
) {
2333 case RECURSIVE_ALWAYS
:
2337 case RECURSIVE_NEVER
:
2340 case RECURSIVE_ABORT
:
2347 #ifdef WITH_BACKGROUND
2348 static FileProgressStatus
2349 do_file_error (const char *str
)
2353 FileProgressStatus (*f
) (enum OperationMode
, const char *);
2355 pntr
.f
= real_do_file_error
;
2357 if (we_are_background
)
2358 return parent_call (pntr
.p
, NULL
, 1, strlen (str
),
2361 return real_do_file_error (Foreground
, str
);
2364 static FileProgressStatus
2365 query_recursive (FileOpContext
*ctx
, const char *s
)
2369 FileProgressStatus (*f
) (FileOpContext
*, enum OperationMode
, const char *);
2371 pntr
.f
= real_query_recursive
;
2373 if (we_are_background
)
2374 return parent_call (pntr
.p
, ctx
, 1, strlen (s
), s
);
2376 return real_query_recursive (ctx
, Foreground
, s
);
2379 static FileProgressStatus
2380 query_replace (FileOpContext
*ctx
, const char *destname
, struct stat
*_s_stat
,
2381 struct stat
*_d_stat
)
2385 FileProgressStatus (*f
) (FileOpContext
*, enum OperationMode
, const char *,
2386 struct stat
*, struct stat
*);
2388 pntr
.f
= file_progress_real_query_replace
;
2390 if (we_are_background
)
2391 return parent_call (pntr
.p
,
2394 strlen (destname
), destname
,
2395 sizeof (struct stat
), _s_stat
,
2396 sizeof (struct stat
), _d_stat
);
2398 return file_progress_real_query_replace (ctx
, Foreground
, destname
,
2403 static FileProgressStatus
2404 do_file_error (const char *str
)
2406 return real_do_file_error (Foreground
, str
);
2409 static FileProgressStatus
2410 query_recursive (FileOpContext
*ctx
, const char *s
)
2412 return real_query_recursive (ctx
, Foreground
, s
);
2415 static FileProgressStatus
2416 query_replace (FileOpContext
*ctx
, const char *destname
, struct stat
*_s_stat
,
2417 struct stat
*_d_stat
)
2419 return file_progress_real_query_replace (ctx
, Foreground
, destname
,
2423 #endif /* !WITH_BACKGROUND */
2426 Cause emacs to enter folding mode for this file: