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
**linklist
)
180 struct link
*lp
, *lp2
;
182 for (lp
= *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 warn_same_file (const char *fmt
, const char *a
, const char *b
)
388 msg
= g_strdup_printf (fmt
, a
, b
);
389 result
= query_dialog (MSG_ERROR
, msg
, D_ERROR
, 2, _("&Skip"), _("&Abort"));
392 if ( result
) { /* 1 == Abort */
400 copy_file_file (FileOpContext
*ctx
, const char *src_path
, const char *dst_path
,
401 int ask_overwrite
, off_t
*progress_count
,
402 double *progress_bytes
, int is_toplevel_file
)
404 uid_t src_uid
= (uid_t
) - 1;
405 gid_t src_gid
= (gid_t
) - 1;
408 int buf_size
= BUF_8K
;
409 int src_desc
, dest_desc
= -1;
410 int n_read
, n_written
;
411 mode_t src_mode
= 0; /* The mode of the source file */
414 int dst_exists
= 0, appending
= 0;
415 off_t n_read_total
= 0, file_size
= -1;
416 FileProgressStatus return_status
, temp_status
;
417 struct timeval tv_transfer_start
;
418 int dst_status
= DEST_NONE
; /* 1 if the file is not fully copied */
421 /* FIXME: We should not be using global variables! */
423 return_status
= FILE_RETRY
;
425 if (file_progress_show_source (ctx
, src_path
) == FILE_ABORT
||
426 file_progress_show_target (ctx
, dst_path
) == FILE_ABORT
)
431 while (mc_stat (dst_path
, &sb2
) == 0) {
432 if (S_ISDIR (sb2
.st_mode
)) {
434 file_error (_(" Cannot overwrite directory \"%s\" \n %s "),
436 if (return_status
== FILE_RETRY
)
438 return return_status
;
444 while ((*ctx
->stat_func
) (src_path
, &sb
)) {
446 file_error (_(" Cannot stat source file \"%s\" \n %s "),
448 if (return_status
!= FILE_RETRY
)
449 return return_status
;
453 /* Destination already exists */
454 if (sb
.st_dev
== sb2
.st_dev
&& sb
.st_ino
== sb2
.st_ino
)
455 return warn_same_file (_(" `%s' \n and \n `%s' \n are the same file "),
457 /* Should we replace destination? */
460 return_status
= query_replace (ctx
, dst_path
, &sb
, &sb2
);
461 if (return_status
!= FILE_CONT
)
462 return return_status
;
466 if (!ctx
->do_append
) {
467 /* Check the hardlinks */
468 if (!ctx
->follow_links
&& sb
.st_nlink
> 1 &&
469 check_hardlinks (src_path
, dst_path
, &sb
) == 1) {
470 /* We have made a hardlink - no more processing is necessary */
474 if (S_ISLNK (sb
.st_mode
))
475 return make_symlink (ctx
, src_path
, dst_path
);
477 if (S_ISCHR (sb
.st_mode
) || S_ISBLK (sb
.st_mode
) ||
478 S_ISFIFO (sb
.st_mode
) || S_ISNAM (sb
.st_mode
) ||
479 S_ISSOCK (sb
.st_mode
)) {
480 while (mc_mknod (dst_path
, sb
.st_mode
& ctx
->umask_kill
,
482 return_status
= file_error (
483 _(" Cannot create special file \"%s\" \n %s "), dst_path
);
484 if (return_status
== FILE_RETRY
)
486 return return_status
;
490 while (ctx
->preserve_uidgid
491 && mc_chown (dst_path
, sb
.st_uid
, sb
.st_gid
)) {
492 temp_status
= file_error (
493 _(" Cannot chown target file \"%s\" \n %s "), dst_path
);
494 if (temp_status
== FILE_RETRY
)
498 while (ctx
->preserve
&&
499 mc_chmod (dst_path
, sb
.st_mode
& ctx
->umask_kill
)) {
500 temp_status
= file_error (
501 _(" Cannot chmod target file \"%s\" \n %s "), dst_path
);
502 if (temp_status
== FILE_RETRY
)
510 gettimeofday (&tv_transfer_start
, (struct timezone
*) NULL
);
512 while ((src_desc
= mc_open (src_path
, O_RDONLY
| O_LINEAR
)) < 0) {
513 return_status
= file_error (
514 _(" Cannot open source file \"%s\" \n %s "), src_path
);
515 if (return_status
== FILE_RETRY
)
518 return return_status
;
522 if (mc_lseek (src_desc
, ctx
->do_reget
, SEEK_SET
) != ctx
->do_reget
) {
523 message (D_ERROR
, _("Warning"),
524 _(" Reget failed, about to overwrite file "));
525 ctx
->do_reget
= ctx
->do_append
= 0;
529 while (mc_fstat (src_desc
, &sb
)) {
530 return_status
= file_error (
531 _(" Cannot fstat source file \"%s\" \n %s "), src_path
);
532 if (return_status
== FILE_RETRY
)
537 src_mode
= sb
.st_mode
;
540 utb
.actime
= sb
.st_atime
;
541 utb
.modtime
= sb
.st_mtime
;
542 file_size
= sb
.st_size
;
544 open_flags
= O_WRONLY
;
545 if (dst_exists
!= 0) {
546 if (ctx
->do_append
!= 0)
547 open_flags
|= O_APPEND
;
549 open_flags
|= O_CREAT
| O_TRUNC
;
551 open_flags
|= O_CREAT
| O_EXCL
;
553 while ((dest_desc
= mc_open (dst_path
, open_flags
, src_mode
)) < 0) {
554 if (errno
== EEXIST
) {
557 return_status
= file_error (
558 _(" Cannot create target file \"%s\" \n %s "), dst_path
);
559 if (return_status
== FILE_RETRY
)
564 dst_status
= DEST_SHORT
; /* file opened, but not fully copied */
566 appending
= ctx
->do_append
;
569 /* Find out the optimal buffer size. */
570 while (mc_fstat (dest_desc
, &sb
)) {
571 return_status
= file_error (
572 _(" Cannot fstat target file \"%s\" \n %s "), dst_path
);
573 if (return_status
== FILE_RETRY
)
577 buf
= g_malloc (buf_size
);
582 return_status
= file_progress_show (ctx
, 0, file_size
);
586 if (return_status
!= FILE_CONT
)
590 struct timeval tv_current
, tv_last_update
, tv_last_input
;
591 int secs
, update_secs
;
593 const char *stalled_msg
;
595 tv_last_update
= tv_transfer_start
;
599 if (mc_ctl (src_desc
, VFS_CTL_IS_NOTREADY
, 0))
602 while ((n_read
= mc_read (src_desc
, buf
, buf_size
)) < 0) {
603 return_status
= file_error (
604 _(" Cannot read source file \"%s\" \n %s "), src_path
);
605 if (return_status
== FILE_RETRY
)
612 gettimeofday (&tv_current
, NULL
);
616 n_read_total
+= n_read
;
618 /* Windows NT ftp servers report that files have no
619 * permissions: -------, so if we happen to have actually
620 * read something, we should fix the permissions.
622 if (!(src_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
)))
623 src_mode
= S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
;
624 gettimeofday (&tv_last_input
, NULL
);
628 mc_write (dest_desc
, t
, n_read
)) < n_read
) {
635 file_error (_(" Cannot write target file \"%s\" \n %s "),
637 if (return_status
!= FILE_RETRY
)
642 /* 1. Update rotating dash after some time (hardcoded to 2 seconds) */
643 secs
= (tv_current
.tv_sec
- tv_last_update
.tv_sec
);
646 tv_last_update
= tv_current
;
649 /* 2. Check for a stalled condition */
650 update_secs
= (tv_current
.tv_sec
- tv_last_input
.tv_sec
);
652 if (update_secs
> 4) {
653 stalled_msg
= _("(stalled)");
658 dt
= (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
662 ((dt
/ (double) n_read_total
) * file_size
) - dt
;
663 ctx
->bps
= n_read_total
/ ((dt
< 1) ? 1 : dt
);
668 /* 4. Compute BPS rate */
671 (tv_current
.tv_sec
- tv_transfer_start
.tv_sec
);
672 if (ctx
->bps_time
< 1)
674 ctx
->bps
= n_read_total
/ ctx
->bps_time
;
677 file_progress_set_stalled_label (ctx
, stalled_msg
);
678 return_status
= file_progress_show_bytes (ctx
, *progress_bytes
+
679 n_read_total
+ ctx
->do_reget
, ctx
->progress_bytes
);
680 if (return_status
== FILE_CONT
) {
682 file_progress_show (ctx
, n_read_total
+ ctx
->do_reget
, file_size
);
685 if (return_status
!= FILE_CONT
)
690 dst_status
= DEST_FULL
; /* copy successful, don't remove target file */
695 while (src_desc
!= -1 && mc_close (src_desc
) < 0) {
696 temp_status
= file_error (
697 _(" Cannot close source file \"%s\" \n %s "), src_path
);
698 if (temp_status
== FILE_RETRY
)
700 if (temp_status
== FILE_ABORT
)
701 return_status
= temp_status
;
705 while (dest_desc
!= -1 && mc_close (dest_desc
) < 0) {
706 temp_status
= file_error (
707 _(" Cannot close target file \"%s\" \n %s "), dst_path
);
708 if (temp_status
== FILE_RETRY
)
710 return_status
= temp_status
;
714 if (dst_status
== DEST_SHORT
) {
715 /* Remove short file */
717 result
= query_dialog (Q_("DialogTitle|Copy"),
718 _("Incomplete file was retrieved. Keep it?"),
719 D_ERROR
, 2, _("&Delete"), _("&Keep"));
721 mc_unlink (dst_path
);
722 } else if (dst_status
== DEST_FULL
) {
723 /* Copy has succeeded */
724 if (!appending
&& ctx
->preserve_uidgid
) {
725 while (mc_chown (dst_path
, src_uid
, src_gid
)) {
726 temp_status
= file_error (
727 _(" Cannot chown target file \"%s\" \n %s "), dst_path
);
728 if (temp_status
== FILE_RETRY
)
730 return_status
= temp_status
;
737 while (mc_chmod (dst_path
, (src_mode
& ctx
->umask_kill
))) {
738 temp_status
= file_error (
739 _(" Cannot chmod target file \"%s\" \n %s "), dst_path
);
740 if (temp_status
!= FILE_RETRY
) {
741 return_status
= temp_status
;
746 src_mode
= umask(-1);
748 src_mode
= 0100666 & ~src_mode
;
749 mc_chmod (dst_path
, (src_mode
& ctx
->umask_kill
));
751 mc_utime (dst_path
, &utb
);
755 if (return_status
== FILE_CONT
)
757 progress_update_one (ctx
, progress_count
, progress_bytes
,
758 file_size
, is_toplevel_file
);
760 return return_status
;
764 * I think these copy_*_* functions should have a return type.
765 * anyway, this function *must* have two directories as arguments.
767 /* FIXME: This function needs to check the return values of the
770 copy_dir_dir (FileOpContext
*ctx
, const char *s
, const char *_d
, int toplevel
,
771 int move_over
, int delete, struct link
*parent_dirs
,
772 off_t
*progress_count
, double *progress_bytes
)
775 struct stat buf
, cbuf
;
777 char *dest_dir
= NULL
;
778 FileProgressStatus return_status
= FILE_CONT
;
783 d
= strutils_shell_unescape (_d
);
785 /* First get the mode of the source dir */
787 if ((*ctx
->stat_func
) (s
, &cbuf
)) {
789 file_error (_(" Cannot stat source directory \"%s\" \n %s "), s
);
790 if (return_status
== FILE_RETRY
)
795 if (is_in_linklist (dest_dirs
, s
, &cbuf
)) {
796 /* Don't copy a directory we created before (we don't want to copy
797 infinitely if a directory is copied into itself) */
798 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
799 return_status
= FILE_CONT
;
803 /* Hmm, hardlink to directory??? - Norbert */
804 /* FIXME: In this step we should do something
805 in case the destination already exist */
806 /* Check the hardlinks */
807 if (ctx
->preserve
&& cbuf
.st_nlink
> 1
808 && check_hardlinks (s
, d
, &cbuf
) == 1) {
809 /* We have made a hardlink - no more processing is necessary */
813 if (!S_ISDIR (cbuf
.st_mode
)) {
815 file_error (_(" Source \"%s\" is not a directory \n %s "), s
);
816 if (return_status
== FILE_RETRY
)
821 if (is_in_linklist (parent_dirs
, s
, &cbuf
)) {
822 /* we found a cyclic symbolic link */
823 message (D_ERROR
, MSG_ERROR
,
824 _(" Cannot copy cyclic symbolic link \n `%s' "), s
);
825 return_status
= FILE_SKIP
;
829 lp
= g_new (struct link
, 1);
830 lp
->vfs
= vfs_get_class (s
);
831 lp
->ino
= cbuf
.st_ino
;
832 lp
->dev
= cbuf
.st_dev
;
833 lp
->next
= parent_dirs
;
837 /* Now, check if the dest dir exists, if not, create it. */
838 if (mc_stat (d
, &buf
)) {
839 /* Here the dir doesn't exist : make it ! */
841 if (mc_rename (s
, d
) == 0) {
842 return_status
= FILE_CONT
;
850 * If the destination directory exists, we want to copy the whole
851 * directory, but we only want this to happen once.
853 * Escape sequences added to the * to compiler warnings.
854 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
855 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
857 if (!S_ISDIR (buf
.st_mode
)) {
858 return_status
= file_error(
859 _(" Destination \"%s\" must be a directory \n %s "), d
);
860 if (return_status
== FILE_RETRY
)
864 /* Dive into subdir if exists */
865 if (toplevel
&& ctx
->dive_into_subdirs
) {
866 dest_dir
= concat_dir_and_file (d
, x_basename (s
));
873 while (my_mkdir (dest_dir
, (cbuf
.st_mode
& ctx
->umask_kill
) | S_IRWXU
)) {
874 return_status
= file_error (
875 _(" Cannot create target directory \"%s\" \n %s "), dest_dir
);
876 if (return_status
!= FILE_RETRY
)
880 lp
= g_new (struct link
, 1);
881 mc_stat (dest_dir
, &buf
);
882 lp
->vfs
= vfs_get_class (dest_dir
);
883 lp
->ino
= buf
.st_ino
;
884 lp
->dev
= buf
.st_dev
;
885 lp
->next
= dest_dirs
;
888 if (ctx
->preserve_uidgid
) {
889 while (mc_chown (dest_dir
, cbuf
.st_uid
, cbuf
.st_gid
)) {
890 return_status
= file_error (
891 _(" Cannot chown target directory \"%s\" \n %s "), dest_dir
);
892 if (return_status
!= FILE_RETRY
)
898 /* open the source dir for reading */
899 reading
= mc_opendir (s
);
903 while ((next
= mc_readdir (reading
)) && return_status
!= FILE_ABORT
) {
906 * Now, we don't want '.' and '..' to be created / copied at any time
908 if (!strcmp (next
->d_name
, "."))
910 if (!strcmp (next
->d_name
, ".."))
913 /* get the filename and add it to the src directory */
914 path
= concat_dir_and_file (s
, next
->d_name
);
916 (*ctx
->stat_func
) (path
, &buf
);
917 if (S_ISDIR (buf
.st_mode
)) {
920 mdpath
= concat_dir_and_file (dest_dir
, next
->d_name
);
922 * From here, we just intend to recursively copy subdirs, not
923 * the double functionality of copying different when the target
924 * dir already exists. So, we give the recursive call the flag 0
925 * meaning no toplevel.
927 return_status
= copy_dir_dir (ctx
, path
, mdpath
, 0, 0, delete,
928 parent_dirs
, progress_count
, progress_bytes
);
933 dest_file
= concat_dir_and_file (dest_dir
, x_basename (path
));
934 return_status
= copy_file_file (ctx
, path
, dest_file
, 1,
935 progress_count
, progress_bytes
, 0);
938 if (delete && return_status
== FILE_CONT
) {
939 if (ctx
->erase_at_end
) {
940 static struct link
*tail
;
941 size_t len
= strlen (path
);
942 lp
= g_malloc (sizeof (struct link
) + len
);
943 strncpy (lp
->name
, path
, len
+ 1);
944 lp
->st_mode
= buf
.st_mode
;
946 if (erase_list
!= NULL
) {
950 erase_list
= tail
= lp
;
952 if (S_ISDIR (buf
.st_mode
)) {
953 return_status
= erase_dir_iff_empty (ctx
, path
);
955 return_status
= erase_file (ctx
, path
, 0, 0, 0);
960 mc_closedir (reading
);
963 mc_chmod (dest_dir
, cbuf
.st_mode
& ctx
->umask_kill
);
964 utb
.actime
= cbuf
.st_atime
;
965 utb
.modtime
= cbuf
.st_mtime
;
966 mc_utime (dest_dir
, &utb
);
968 cbuf
.st_mode
= umask(-1);
970 cbuf
.st_mode
= 0100777 & ~cbuf
.st_mode
;
971 mc_chmod (dest_dir
, cbuf
.st_mode
& ctx
->umask_kill
);
976 g_free (parent_dirs
);
979 return return_status
;
984 /* {{{ Move routines */
986 static FileProgressStatus
987 move_file_file (FileOpContext
*ctx
, const char *s
, const char *d
,
988 off_t
*progress_count
, double *progress_bytes
)
990 struct stat src_stats
, dst_stats
;
991 FileProgressStatus return_status
= FILE_CONT
;
992 gboolean copy_done
= FALSE
;
994 if (file_progress_show_source (ctx
, s
) == FILE_ABORT
995 || file_progress_show_target (ctx
, d
) == FILE_ABORT
)
1000 while (mc_lstat (s
, &src_stats
) != 0) {
1001 /* Source doesn't exist */
1003 file_error (_(" Cannot stat file \"%s\" \n %s "), s
);
1004 if (return_status
!= FILE_RETRY
)
1005 return return_status
;
1008 if (mc_lstat (d
, &dst_stats
) == 0) {
1009 if (src_stats
.st_dev
== dst_stats
.st_dev
1010 && src_stats
.st_ino
== dst_stats
.st_ino
)
1011 return warn_same_file (_(" `%s' \n and \n `%s' \n are the same file "), s
, d
);
1013 if (S_ISDIR (dst_stats
.st_mode
)) {
1014 message (D_ERROR
, MSG_ERROR
,
1015 _(" Cannot overwrite directory `%s' "), d
);
1020 if (confirm_overwrite
) {
1021 return_status
= query_replace (ctx
, d
, &src_stats
, &dst_stats
);
1022 if (return_status
!= FILE_CONT
)
1023 return return_status
;
1025 /* Ok to overwrite */
1028 if (!ctx
->do_append
) {
1029 if (S_ISLNK (src_stats
.st_mode
) && ctx
->stable_symlinks
) {
1030 if ((return_status
= make_symlink (ctx
, s
, d
)) == FILE_CONT
) {
1031 goto retry_src_remove
;
1033 return return_status
;
1036 if (mc_rename (s
, d
) == 0) {
1037 return progress_update_one (ctx
, progress_count
,
1039 src_stats
.st_size
, 1);
1043 /* Comparison to EXDEV seems not to work in nfs if you're moving from
1044 one nfs to the same, but on the server it is on two different
1045 filesystems. Then nfs returns EIO instead of EXDEV.
1046 Hope it will not hurt if we always in case of error try to copy/delete. */
1048 errno
= EXDEV
; /* Hack to copy (append) the file and then delete it */
1050 if (errno
!= EXDEV
) {
1052 files_error (_(" Cannot move file \"%s\" to \"%s\" \n %s "), s
,
1054 if (return_status
== FILE_RETRY
)
1056 return return_status
;
1060 /* Failed because filesystem boundary -> copy the file instead */
1062 copy_file_file (ctx
, s
, d
, 0, progress_count
, progress_bytes
, 1);
1063 if (return_status
!= FILE_CONT
)
1064 return return_status
;
1068 if ((return_status
=
1069 file_progress_show_source (ctx
, NULL
)) != FILE_CONT
1070 || (return_status
= file_progress_show (ctx
, 0, 0)) != FILE_CONT
)
1071 return return_status
;
1076 if (mc_unlink (s
)) {
1078 file_error (_(" Cannot remove file \"%s\" \n %s "), s
);
1079 if (return_status
== FILE_RETRY
)
1080 goto retry_src_remove
;
1081 return return_status
;
1085 return_status
= progress_update_one (ctx
,
1088 src_stats
.st_size
, 1);
1091 return return_status
;
1095 move_dir_dir (FileOpContext
*ctx
, const char *s
, const char *d
,
1096 off_t
*progress_count
, double *progress_bytes
)
1098 struct stat sbuf
, dbuf
, destbuf
;
1101 FileProgressStatus return_status
;
1102 gboolean move_over
= FALSE
;
1105 if (file_progress_show_source (ctx
, s
) == FILE_ABORT
||
1106 file_progress_show_target (ctx
, d
) == FILE_ABORT
)
1112 dstat_ok
= (mc_stat (d
, &dbuf
) == 0);
1114 if (dstat_ok
&& sbuf
.st_dev
== dbuf
.st_dev
&& sbuf
.st_ino
== dbuf
.st_ino
)
1115 return warn_same_file (_(" `%s' \n and \n `%s' \n are the same directory "), s
, d
);
1118 destdir
= g_strdup (d
); /* destination doesn't exist */
1119 else if (!ctx
->dive_into_subdirs
) {
1120 destdir
= g_strdup (d
);
1123 destdir
= concat_dir_and_file (d
, x_basename (s
));
1125 /* Check if the user inputted an existing dir */
1127 if (!mc_stat (destdir
, &destbuf
)) {
1129 return_status
= copy_dir_dir (ctx
, s
, destdir
, 0, 1, 1, 0,
1130 progress_count
, progress_bytes
);
1132 if (return_status
!= FILE_CONT
)
1136 if (S_ISDIR (destbuf
.st_mode
))
1139 (" Cannot overwrite directory \"%s\" %s "),
1143 file_error (_(" Cannot overwrite file \"%s\" %s "),
1145 if (return_status
== FILE_RETRY
)
1146 goto retry_dst_stat
;
1149 return return_status
;
1153 if (mc_rename (s
, destdir
) == 0) {
1154 return_status
= FILE_CONT
;
1158 if (errno
!= EXDEV
) {
1161 (" Cannot move directory \"%s\" to \"%s\" \n %s "),
1163 if (return_status
== FILE_RETRY
)
1167 /* Failed because of filesystem boundary -> copy dir instead */
1169 copy_dir_dir (ctx
, s
, destdir
, 0, 0, 1, 0, progress_count
,
1172 if (return_status
!= FILE_CONT
)
1175 if ((return_status
=
1176 file_progress_show_source (ctx
, NULL
)) != FILE_CONT
1177 || (return_status
= file_progress_show (ctx
, 0, 0)) != FILE_CONT
)
1181 if (ctx
->erase_at_end
) {
1182 for (; erase_list
&& return_status
!= FILE_ABORT
;) {
1183 if (S_ISDIR (erase_list
->st_mode
)) {
1185 erase_dir_iff_empty (ctx
, erase_list
->name
);
1188 erase_file (ctx
, erase_list
->name
, 0, 0, 0);
1190 erase_list
= erase_list
->next
;
1194 erase_dir_iff_empty (ctx
, s
);
1198 while (erase_list
) {
1200 erase_list
= erase_list
->next
;
1203 return return_status
;
1208 /* {{{ Erase routines */
1209 /* Don't update progress status if progress_count==NULL */
1210 static FileProgressStatus
1211 erase_file (FileOpContext
*ctx
, const char *s
, off_t
*progress_count
,
1212 double *progress_bytes
, int is_toplevel_file
)
1217 if (file_progress_show_deleting (ctx
, s
) == FILE_ABORT
)
1221 if (progress_count
&& mc_lstat (s
, &buf
)) {
1222 /* ignore, most likely the mc_unlink fails, too */
1226 while (mc_unlink (s
)) {
1228 file_error (_(" Cannot delete file \"%s\" \n %s "), s
);
1229 if (return_status
!= FILE_RETRY
)
1230 return return_status
;
1234 return progress_update_one (ctx
, progress_count
, progress_bytes
,
1235 buf
.st_size
, is_toplevel_file
);
1240 static FileProgressStatus
1241 recursive_erase (FileOpContext
*ctx
, const char *s
, off_t
*progress_count
,
1242 double *progress_bytes
)
1244 struct dirent
*next
;
1248 FileProgressStatus return_status
= FILE_CONT
;
1250 if (!strcmp (s
, ".."))
1253 reading
= mc_opendir (s
);
1258 while ((next
= mc_readdir (reading
)) && return_status
== FILE_CONT
) {
1259 if (!strcmp (next
->d_name
, "."))
1261 if (!strcmp (next
->d_name
, ".."))
1263 path
= concat_dir_and_file (s
, next
->d_name
);
1264 if (mc_lstat (path
, &buf
)) {
1266 mc_closedir (reading
);
1269 if (S_ISDIR (buf
.st_mode
))
1272 (ctx
, path
, progress_count
, progress_bytes
)
1273 != FILE_CONT
) ? FILE_RETRY
: FILE_CONT
;
1276 erase_file (ctx
, path
, progress_count
, progress_bytes
, 0);
1279 mc_closedir (reading
);
1280 if (return_status
!= FILE_CONT
)
1281 return return_status
;
1282 if (file_progress_show_deleting (ctx
, s
) == FILE_ABORT
)
1286 while (my_rmdir (s
)) {
1288 file_error (_(" Cannot remove directory \"%s\" \n %s "), s
);
1289 if (return_status
!= FILE_RETRY
)
1290 return return_status
;
1296 /* Return -1 on error, 1 if there are no entries besides "." and ".."
1297 in the directory path points to, 0 else. */
1299 check_dir_is_empty (const char *path
)
1305 dir
= mc_opendir (path
);
1309 for (i
= 1, d
= mc_readdir (dir
); d
; d
= mc_readdir (dir
)) {
1310 if (d
->d_name
[0] == '.' && (d
->d_name
[1] == '\0' ||
1311 (d
->d_name
[1] == '.'
1312 && d
->d_name
[2] == '\0')))
1313 continue; /* "." or ".." */
1323 erase_dir (FileOpContext
*ctx
, const char *s
, off_t
*progress_count
,
1324 double *progress_bytes
)
1326 FileProgressStatus error
;
1328 if (strcmp (s
, "..") == 0)
1331 if (strcmp (s
, ".") == 0)
1334 if (file_progress_show_deleting (ctx
, s
) == FILE_ABORT
)
1338 /* The old way to detect a non empty directory was:
1339 error = my_rmdir (s);
1340 if (error && (errno == ENOTEMPTY || errno == EEXIST))){
1341 For the linux user space nfs server (nfs-server-2.2beta29-2)
1342 we would have to check also for EIO. I hope the new way is
1343 fool proof. (Norbert)
1345 error
= check_dir_is_empty (s
);
1346 if (error
== 0) { /* not empty */
1347 error
= query_recursive (ctx
, s
);
1348 if (error
== FILE_CONT
)
1349 return recursive_erase (ctx
, s
, progress_count
,
1355 while (my_rmdir (s
) == -1) {
1357 file_error (_(" Cannot remove directory \"%s\" \n %s "), s
);
1358 if (error
!= FILE_RETRY
)
1365 static FileProgressStatus
1366 erase_dir_iff_empty (FileOpContext
*ctx
, const char *s
)
1368 FileProgressStatus error
;
1370 if (strcmp (s
, "..") == 0)
1373 if (strcmp (s
, ".") == 0)
1376 if (file_progress_show_deleting (ctx
, s
) == FILE_ABORT
)
1380 if (1 != check_dir_is_empty (s
)) /* not empty or error */
1383 while (my_rmdir (s
)) {
1385 file_error (_(" Cannot remove directory \"%s\" \n %s "), s
);
1386 if (error
!= FILE_RETRY
)
1395 /* {{{ Panel operate routines */
1398 * Return currently selected entry name or the name of the first marked
1399 * entry if there is one.
1402 panel_get_file (WPanel
*panel
, struct stat
*stat_buf
)
1406 if (get_current_type () == view_tree
) {
1407 WTree
*tree
= (WTree
*) get_panel_widget (get_current_index ());
1408 char *tree_name
= tree_selected_name (tree
);
1410 mc_stat (tree_name
, stat_buf
);
1414 if (panel
->marked
) {
1415 for (i
= 0; i
< panel
->count
; i
++)
1416 if (panel
->dir
.list
[i
].f
.marked
) {
1417 *stat_buf
= panel
->dir
.list
[i
].st
;
1418 return panel
->dir
.list
[i
].fname
;
1421 *stat_buf
= panel
->dir
.list
[panel
->selected
].st
;
1422 return panel
->dir
.list
[panel
->selected
].fname
;
1424 g_assert_not_reached ();
1430 compute_dir_size_create_ui (void)
1432 ComputeDirSizeUI
*ui
;
1434 const char *b_name
= N_("&Abort");
1440 ui
= g_new (ComputeDirSizeUI
, 1);
1442 ui
->dlg
= create_dlg (0, 0, 8, COLS
/2, dialog_colors
, NULL
,
1443 NULL
, _("Directory scanning"), DLG_CENTER
);
1444 ui
->dirname
= label_new (3, 3, "");
1445 add_widget (ui
->dlg
, ui
->dirname
);
1447 add_widget (ui
->dlg
,
1448 button_new (5, (ui
->dlg
->cols
- strlen (b_name
))/2,
1449 FILE_ABORT
, NORMAL_BUTTON
, b_name
, NULL
));
1451 /* We will manage the dialog without any help,
1452 that's why we have to call init_dlg */
1459 compute_dir_size_destroy_ui (ComputeDirSizeUI
*ui
)
1462 /* schedule to update passive panel */
1463 other_panel
->dirty
= 1;
1465 /* close and destroy dialog */
1466 dlg_run_done (ui
->dlg
);
1467 destroy_dlg (ui
->dlg
);
1473 compute_dir_size_update_ui (const void *ui
, const char *dirname
)
1475 const ComputeDirSizeUI
*this = (const ComputeDirSizeUI
*) ui
;
1482 label_set_text (this->dirname
, name_trunc (dirname
, this->dlg
->cols
- 6));
1484 event
.x
= -1; /* Don't show the GPM cursor */
1485 c
= tty_get_event (&event
, FALSE
, FALSE
);
1489 /* Reinitialize to avoid old values after events other than
1490 selecting a button */
1491 this->dlg
->ret_value
= FILE_CONT
;
1493 dlg_process_event (this->dlg
, c
, &event
);
1495 switch (this->dlg
->ret_value
) {
1507 * Computes the number of bytes used by the files in a directory
1510 compute_dir_size (const char *dirname
, const void *ui
,
1511 compute_dir_size_callback cback
,
1512 off_t
*ret_marked
, double *ret_total
)
1515 struct dirent
*dirent
;
1516 FileProgressStatus ret
= FILE_CONT
;
1518 dir
= mc_opendir (dirname
);
1523 while ((dirent
= mc_readdir (dir
)) != NULL
) {
1528 ret
= (cback
!= NULL
) ? cback (ui
, dirname
) : FILE_CONT
;
1530 if (ret
!= FILE_CONT
)
1533 if (strcmp (dirent
->d_name
, ".") == 0)
1535 if (strcmp (dirent
->d_name
, "..") == 0)
1538 fullname
= concat_dir_and_file (dirname
, dirent
->d_name
);
1539 res
= mc_lstat (fullname
, &s
);
1546 if (S_ISDIR (s
.st_mode
)) {
1547 off_t subdir_count
= 0;
1548 double subdir_bytes
= 0;
1550 ret
= compute_dir_size (fullname
, ui
, cback
, &subdir_count
, &subdir_bytes
);
1552 if (ret
!= FILE_CONT
) {
1557 *ret_marked
+= subdir_count
;
1558 *ret_total
+= subdir_bytes
;
1561 *ret_total
+= s
.st_size
;
1573 * panel_compute_totals:
1575 * compute the number of files and the number of bytes
1576 * used up by the whole selection, recursing directories
1577 * as required. In addition, it checks to see if it will
1578 * overwrite any files by doing the copy.
1580 static FileProgressStatus
1581 panel_compute_totals (WPanel
*panel
, const void *ui
,
1582 compute_dir_size_callback cback
,
1583 off_t
*ret_marked
, double *ret_total
)
1590 for (i
= 0; i
< panel
->count
; i
++) {
1593 if (!panel
->dir
.list
[i
].f
.marked
)
1596 s
= &panel
->dir
.list
[i
].st
;
1598 if (S_ISDIR (s
->st_mode
)) {
1600 off_t subdir_count
= 0;
1601 double subdir_bytes
= 0;
1602 FileProgressStatus status
;
1605 concat_dir_and_file (panel
->cwd
, panel
->dir
.list
[i
].fname
);
1607 status
= compute_dir_size (dir_name
, ui
, cback
,
1608 &subdir_count
, &subdir_bytes
);
1611 if (status
!= FILE_CONT
)
1614 *ret_marked
+= subdir_count
;
1615 *ret_total
+= subdir_bytes
;
1618 *ret_total
+= s
->st_size
;
1626 * This array introduced to avoid translation problems. The former (op_names)
1627 * is assumed to be nouns, suitable in dialog box titles; this one should
1628 * contain whatever is used in prompt itself (i.e. in russian, it's verb).
1629 * (I don't use spaces around the words, because someday they could be
1630 * dropped, when widgets get smarter)
1633 /* TRANSLATORS: no need to translate 'FileOperation', it's just a context prefix */
1634 static const char *op_names1
[] = {
1635 N_("FileOperation|Copy"),
1636 N_("FileOperation|Move"),
1637 N_("FileOperation|Delete")
1641 * These are formats for building a prompt. Parts encoded as follows:
1642 * %o - operation from op_names1
1643 * %f - file/files or files/directories, as appropriate
1644 * %m - "with source mask" or question mark for delete
1645 * %s - source name (truncated)
1646 * %d - number of marked files
1647 * %e - "to:" or question mark for delete
1649 * xgettext:no-c-format */
1650 static const char *one_format
= N_("%o %f \"%s\"%m");
1651 /* xgettext:no-c-format */
1652 static const char *many_format
= N_("%o %d %f%m");
1654 static const char *prompt_parts
[] = {
1659 N_("files/directories"),
1660 N_(" with source mask:"),
1664 static const char *question_format
= N_("%s?");
1667 * Generate user prompt for panel operation.
1668 * single_source is the name if the source entry or NULL for multiple
1670 * src_stat is only used when single_source is not NULL.
1673 panel_operate_generate_prompt (const WPanel
*panel
, const int operation
,
1674 gboolean single_source
,
1675 const struct stat
*src_stat
)
1677 const char *sp
, *cp
;
1679 char format_string
[BUF_MEDIUM
];
1680 char *dp
= format_string
;
1681 gboolean build_question
= FALSE
;
1684 static gboolean i18n_flag
= FALSE
;
1686 for (i
= sizeof (op_names1
) / sizeof (op_names1
[0]); i
--;)
1687 op_names1
[i
] = Q_(op_names1
[i
]);
1689 for (i
= sizeof (prompt_parts
) / sizeof (prompt_parts
[0]); i
--;)
1690 prompt_parts
[i
] = _(prompt_parts
[i
]);
1692 one_format
= _(one_format
);
1693 many_format
= _(many_format
);
1694 question_format
= _(question_format
);
1697 #endif /* ENABLE_NLS */
1699 sp
= single_source
? one_format
: many_format
;
1701 while (*sp
!= '\0') {
1707 cp
= op_names1
[operation
];
1710 if (operation
== OP_DELETE
) {
1712 build_question
= TRUE
;
1714 cp
= prompt_parts
[5];
1717 if (operation
== OP_DELETE
) {
1719 build_question
= TRUE
;
1721 cp
= prompt_parts
[6];
1724 if (single_source
) {
1725 cp
= S_ISDIR (src_stat
->
1726 st_mode
) ? prompt_parts
[2] : prompt_parts
[0];
1728 cp
= (panel
->marked
== panel
->dirs_marked
)
1730 : (panel
->dirs_marked
? prompt_parts
[4] : prompt_parts
[1]);
1748 if (build_question
) {
1749 char tmp
[BUF_MEDIUM
];
1751 memmove (tmp
, format_string
, sizeof (tmp
));
1752 g_snprintf (format_string
, sizeof (format_string
),
1753 question_format
, tmp
);
1756 return g_strdup (format_string
);
1762 * Performs one of the operations on the selection on the source_panel
1763 * (copy, delete, move).
1765 * Returns 1 if did change the directory
1766 * structure, Returns 0 if user aborted
1768 * force_single forces operation on the current entry and affects
1769 * default destination. Current filename is used as default.
1772 panel_operate (void *source_panel
, FileOperation operation
,
1775 WPanel
*panel
= source_panel
;
1776 char *source
= NULL
;
1777 #ifdef WITH_FULL_PATHS
1778 char *source_with_path
= NULL
;
1780 # define source_with_path source
1781 #endif /* !WITH_FULL_PATHS */
1784 char *save_cwd
= NULL
, *save_dest
= NULL
;
1785 int single_entry
= (get_current_type () == view_tree
)
1786 || (panel
->marked
<= 1) || force_single
;
1787 struct stat src_stat
, dst_stat
;
1789 FileProgressStatus value
;
1796 int do_bg
= 0; /* do background operation? */
1798 free_linklist (&linklist
);
1799 free_linklist (&dest_dirs
);
1801 /* Update panel contents to avoid actions on deleted files */
1802 if (!panel
->is_panelized
) {
1803 update_panels (UP_RELOAD
, UP_KEEPSEL
);
1809 source
= selection (panel
)->fname
;
1810 src_stat
= selection (panel
)->st
;
1812 source
= panel_get_file (panel
, &src_stat
);
1815 if (!strcmp (source
, "..")) {
1816 message (D_ERROR
, MSG_ERROR
, _(" Cannot operate on \"..\"! "));
1821 ctx
= file_op_context_new (operation
);
1823 /* Show confirmation dialog */
1824 if (operation
!= OP_DELETE
) {
1829 /* Forced single operations default to the original name */
1832 else if (get_other_type () == view_listing
)
1833 dest_dir
= other_panel
->cwd
;
1835 dest_dir
= panel
->cwd
;
1837 * Add trailing backslash only when do non-local ops.
1838 * It saves user from occasional file renames (when destination
1842 && dest_dir
[0] != '\0'
1843 && dest_dir
[strlen (dest_dir
) - 1] != PATH_SEP
) {
1844 /* add trailing separator */
1845 dest_dir_
= g_strconcat (dest_dir
, PATH_SEP_STR
, (char *) NULL
);
1848 dest_dir_
= g_strdup (dest_dir
);
1850 if (dest_dir_
== NULL
) {
1851 file_op_context_destroy (ctx
);
1855 /* Generate confirmation prompt */
1856 format
= panel_operate_generate_prompt (panel
, operation
,
1857 source
!= NULL
, &src_stat
);
1859 dest
= file_mask_dialog (ctx
, operation
, source
!= NULL
, format
,
1860 source
!= NULL
? (void *) source
1861 : (void *) &panel
->marked
,
1867 if (dest
== NULL
|| dest
[0] == '\0') {
1868 file_op_context_destroy (ctx
);
1872 } else if (confirm_delete
) {
1874 char fmd_buf
[BUF_MEDIUM
];
1876 /* Generate confirmation prompt */
1877 format
= panel_operate_generate_prompt (panel
, OP_DELETE
,
1878 source
!= NULL
, &src_stat
);
1881 g_snprintf (fmd_buf
, sizeof (fmd_buf
), format
, panel
->marked
);
1883 const int fmd_xlen
= 64;
1884 i
= fmd_xlen
- str_term_width1 (format
) - 4;
1885 g_snprintf (fmd_buf
, sizeof (fmd_buf
),
1886 format
, str_trunc (source
, i
));
1894 i
= query_dialog (Q_(op_names
[operation
]), fmd_buf
, D_ERROR
, 2,
1895 _("&Yes"), _("&No"));
1898 file_op_context_destroy (ctx
);
1903 #ifdef WITH_BACKGROUND
1904 /* Did the user select to do a background operation? */
1908 v
= do_background (ctx
,
1909 g_strconcat (op_names
[operation
], ": ",
1912 message (D_ERROR
, MSG_ERROR
,
1913 _(" Sorry, I could not put the job in background "));
1916 /* If we are the parent */
1918 mc_setctl (panel
->cwd
, VFS_SETCTL_FORGET
, NULL
);
1919 mc_setctl (dest
, VFS_SETCTL_FORGET
, NULL
);
1920 /* file_op_context_destroy (ctx); */
1924 #endif /* WITH_BACKGROUND */
1926 /* Initialize things */
1927 /* We do not want to trash cache every time file is
1928 created/touched. However, this will make our cache contain
1931 if (mc_setctl (dest
, VFS_SETCTL_STALE_DATA
, (void *) 1))
1932 save_dest
= g_strdup (dest
);
1935 if (mc_setctl (panel
->cwd
, VFS_SETCTL_STALE_DATA
, (void *) 1))
1936 save_cwd
= g_strdup (panel
->cwd
);
1939 /* Now, let's do the job */
1944 file_op_context_create_ui (ctx
, 1);
1946 /* This code is only called by the tree and panel code */
1948 /* We now have ETA in all cases */
1950 /* One file: FIXME mc_chdir will take user out of any vfs */
1951 if (operation
!= OP_COPY
&& get_current_type () == view_tree
)
1952 mc_chdir (PATH_SEP_STR
);
1954 /* The source and src_stat variables have been initialized before */
1955 #ifdef WITH_FULL_PATHS
1956 source_with_path
= concat_dir_and_file (panel
->cwd
, source
);
1957 #endif /* WITH_FULL_PATHS */
1959 if (operation
== OP_DELETE
) {
1960 if (S_ISDIR (src_stat
.st_mode
))
1961 value
= erase_dir (ctx
, source_with_path
, &count
, &bytes
);
1964 erase_file (ctx
, source_with_path
, &count
, &bytes
, 1);
1966 temp
= transform_source (ctx
, source_with_path
);
1968 value
= transform_error
;
1970 char *repl_dest
= mc_search_prepare_replace_str2 (ctx
->search_handle
, dest
);
1971 char *temp2
= concat_dir_and_file (repl_dest
, temp
);
1977 switch (operation
) {
1980 * we use file_mask_op_follow_links only with OP_COPY,
1982 (*ctx
->stat_func
) (source_with_path
, &src_stat
);
1984 if (S_ISDIR (src_stat
.st_mode
)) {
1986 copy_dir_dir (ctx
, source_with_path
, dest
, 1,
1987 0, 0, 0, &count
, &bytes
);
1990 copy_file_file (ctx
, source_with_path
, dest
, 1,
1996 if (S_ISDIR (src_stat
.st_mode
))
1998 move_dir_dir (ctx
, source_with_path
, dest
,
2002 move_file_file (ctx
, source_with_path
, dest
,
2007 /* Unknown file operation */
2011 } /* Copy or move operation */
2013 if ((value
== FILE_CONT
) && !force_single
)
2014 unmark_files (panel
);
2017 /* Check destination for copy or move operation */
2018 if (operation
!= OP_DELETE
) {
2019 retry_many_dst_stat
:
2020 dst_result
= mc_stat (dest
, &dst_stat
);
2021 if (dst_result
== 0 && !S_ISDIR (dst_stat
.st_mode
)) {
2023 (_(" Destination \"%s\" must be a directory \n %s "),
2024 dest
) == FILE_RETRY
)
2025 goto retry_many_dst_stat
;
2030 /* Initialize variables for progress bars */
2031 if (operation
!= OP_MOVE
&& verbose
&& file_op_compute_totals
) {
2032 ComputeDirSizeUI
*ui
;
2033 FileProgressStatus status
;
2035 ui
= compute_dir_size_create_ui ();
2036 status
= panel_compute_totals (panel
,
2037 ui
, compute_dir_size_update_ui
,
2038 &ctx
->progress_count
, &ctx
->progress_bytes
);
2039 compute_dir_size_destroy_ui (ui
);
2041 if (status
!= FILE_CONT
)
2044 ctx
->progress_totals_computed
= 1;
2046 ctx
->progress_totals_computed
= 0;
2047 ctx
->progress_count
= panel
->marked
;
2048 ctx
->progress_bytes
= panel
->total
;
2051 /* Loop for every file, perform the actual copy operation */
2052 for (i
= 0; i
< panel
->count
; i
++) {
2053 if (!panel
->dir
.list
[i
].f
.marked
)
2054 continue; /* Skip the unmarked ones */
2056 source
= panel
->dir
.list
[i
].fname
;
2057 src_stat
= panel
->dir
.list
[i
].st
;
2059 #ifdef WITH_FULL_PATHS
2060 g_free (source_with_path
);
2061 source_with_path
= concat_dir_and_file (panel
->cwd
, source
);
2062 #endif /* WITH_FULL_PATHS */
2064 if (operation
== OP_DELETE
) {
2065 if (S_ISDIR (src_stat
.st_mode
))
2067 erase_dir (ctx
, source_with_path
, &count
, &bytes
);
2070 erase_file (ctx
, source_with_path
, &count
, &bytes
,
2073 temp
= transform_source (ctx
, source_with_path
);
2075 value
= transform_error
;
2078 char *repl_dest
= mc_search_prepare_replace_str2 (ctx
->search_handle
, dest
);
2079 char *temp2
= concat_dir_and_file (repl_dest
, temp
);
2083 temp3
= source_with_path
;
2084 source_with_path
= strutils_shell_unescape(source_with_path
);
2087 temp2
= strutils_shell_unescape(temp2
);
2090 switch (operation
) {
2093 * we use file_mask_op_follow_links only with OP_COPY
2095 (*ctx
->stat_func
) (source_with_path
, &src_stat
);
2096 if (S_ISDIR (src_stat
.st_mode
))
2098 copy_dir_dir (ctx
, source_with_path
, temp2
,
2099 1, 0, 0, 0, &count
, &bytes
);
2102 copy_file_file (ctx
, source_with_path
,
2103 temp2
, 1, &count
, &bytes
,
2105 free_linklist (&dest_dirs
);
2109 if (S_ISDIR (src_stat
.st_mode
))
2111 move_dir_dir (ctx
, source_with_path
, temp2
,
2115 move_file_file (ctx
, source_with_path
,
2116 temp2
, &count
, &bytes
);
2120 /* Unknown file operation */
2125 } /* Copy or move operation */
2127 if (value
== FILE_ABORT
)
2130 if (value
== FILE_CONT
)
2131 do_file_mark (panel
, i
, 0);
2133 if (file_progress_show_count (ctx
, count
, ctx
->progress_count
)
2138 && file_progress_show_bytes (ctx
, bytes
,
2139 ctx
->progress_bytes
) ==
2143 if (operation
!= OP_DELETE
&& verbose
2144 && file_progress_show (ctx
, 0, 0) == FILE_ABORT
)
2148 } /* Loop for every file */
2149 } /* Many entries */
2154 mc_setctl (save_cwd
, VFS_SETCTL_STALE_DATA
, NULL
);
2158 mc_setctl (save_dest
, VFS_SETCTL_STALE_DATA
, NULL
);
2162 free_linklist (&linklist
);
2163 free_linklist (&dest_dirs
);
2164 #ifdef WITH_FULL_PATHS
2165 g_free (source_with_path
);
2166 #endif /* WITH_FULL_PATHS */
2168 g_free (ctx
->dest_mask
);
2169 ctx
->dest_mask
= NULL
;
2170 #ifdef WITH_BACKGROUND
2171 /* Let our parent know we are saying bye bye */
2172 if (we_are_background
) {
2176 #endif /* WITH_BACKGROUND */
2178 file_op_context_destroy (ctx
);
2184 /* {{{ Query/status report routines */
2186 static FileProgressStatus
2187 real_do_file_error (enum OperationMode mode
, const char *error
)
2192 msg
= mode
== Foreground
? MSG_ERROR
: _(" Background process error ");
2194 query_dialog (msg
, error
, D_ERROR
, 3, _("&Skip"), _("&Retry"),
2212 /* Report error with one file */
2214 file_error (const char *format
, const char *file
)
2216 char buf
[BUF_MEDIUM
];
2218 g_snprintf (buf
, sizeof (buf
), format
,
2219 path_trunc (file
, 30), unix_error_string (errno
));
2221 return do_file_error (buf
);
2224 /* Report error with two files */
2225 static FileProgressStatus
2226 files_error (const char *format
, const char *file1
, const char *file2
)
2228 char buf
[BUF_MEDIUM
];
2229 char *nfile1
= g_strdup (path_trunc (file1
, 15));
2230 char *nfile2
= g_strdup (path_trunc (file2
, 15));
2232 g_snprintf (buf
, sizeof (buf
), format
, nfile1
, nfile2
,
2233 unix_error_string (errno
));
2238 return do_file_error (buf
);
2241 static FileProgressStatus
2242 real_query_recursive (FileOpContext
*ctx
, enum OperationMode mode
, const char *s
)
2246 if (ctx
->recursive_result
< RECURSIVE_ALWAYS
) {
2250 _("\n Directory not empty. \n"
2251 " Delete it recursively? ")
2252 : _("\n Background process: Directory not empty \n"
2253 " Delete it recursively? ");
2254 text
= g_strconcat (_(" Delete: "), path_trunc (s
, 30), " ", (char *) NULL
);
2258 ctx
->recursive_result
= query_dialog (text
, msg
, D_ERROR
, 5,
2259 _("&Yes"), _("&No"),
2260 _("A&ll"), _("Non&e"),
2263 if (ctx
->recursive_result
!= RECURSIVE_ABORT
)
2268 switch (ctx
->recursive_result
) {
2270 case RECURSIVE_ALWAYS
:
2274 case RECURSIVE_NEVER
:
2277 case RECURSIVE_ABORT
:
2284 #ifdef WITH_BACKGROUND
2285 static FileProgressStatus
2286 do_file_error (const char *str
)
2290 FileProgressStatus (*f
) (enum OperationMode
, const char *);
2292 pntr
.f
= real_do_file_error
;
2294 if (we_are_background
)
2295 return parent_call (pntr
.p
, NULL
, 1, strlen (str
),
2298 return real_do_file_error (Foreground
, str
);
2301 static FileProgressStatus
2302 query_recursive (FileOpContext
*ctx
, const char *s
)
2306 FileProgressStatus (*f
) (FileOpContext
*, enum OperationMode
, const char *);
2308 pntr
.f
= real_query_recursive
;
2310 if (we_are_background
)
2311 return parent_call (pntr
.p
, ctx
, 1, strlen (s
), s
);
2313 return real_query_recursive (ctx
, Foreground
, s
);
2316 static FileProgressStatus
2317 query_replace (FileOpContext
*ctx
, const char *destname
, struct stat
*_s_stat
,
2318 struct stat
*_d_stat
)
2322 FileProgressStatus (*f
) (FileOpContext
*, enum OperationMode
, const char *,
2323 struct stat
*, struct stat
*);
2325 pntr
.f
= file_progress_real_query_replace
;
2327 if (we_are_background
)
2328 return parent_call (pntr
.p
,
2331 strlen (destname
), destname
,
2332 sizeof (struct stat
), _s_stat
,
2333 sizeof (struct stat
), _d_stat
);
2335 return file_progress_real_query_replace (ctx
, Foreground
, destname
,
2340 static FileProgressStatus
2341 do_file_error (const char *str
)
2343 return real_do_file_error (Foreground
, str
);
2346 static FileProgressStatus
2347 query_recursive (FileOpContext
*ctx
, const char *s
)
2349 return real_query_recursive (ctx
, Foreground
, s
);
2352 static FileProgressStatus
2353 query_replace (FileOpContext
*ctx
, const char *destname
, struct stat
*_s_stat
,
2354 struct stat
*_d_stat
)
2356 return file_progress_real_query_replace (ctx
, Foreground
, destname
,
2360 #endif /* !WITH_BACKGROUND */
2363 Cause emacs to enter folding mode for this file: